日常使用 chatgpt - java - 服务器间拷贝文件

142 阅读10分钟

作为搜索引擎,结果还是比较直观的,

ChatGPT的原理是基于自回归语言模型,我们使用搜索引擎也大概是这个道理,根据模糊词语搜索结果,如果没有找到,再在结果中根据关键词再次查找(树形递归?搜索了一下,是一种算法设计思想)直到找到具体的信息或者根据多个结果总结

我根据树形递归进行了关联查询(分治算法,深度优先搜索,二叉树递归,记忆化搜索)

这里解释一下记忆化搜索, chatgt和记忆化搜索的之间的关系放在另一个文章里,想到啥就搜索啥(向量空间模型)

记忆化搜索是在深度优先搜索的基础上进行优化,它使用一个数组或哈希表来记录已经计算过的结果,以避免重复计算,从而提高算法的效率。该算法需要额外的空间来存储记录已经计算过的结果的数组或哈希表。

因此,记忆化搜索相比于深度优先搜索,可以在搜索速度和空间复杂度之间做出平衡,可以在保持搜索算法时间效率不变的前提下,减少算法的空间占用。但是,由于它需要对计算结果进行存储,所以在计算结果多次被使用的情况下才适用,不适合对整个空间进行遍历。

总之,记忆化搜索和深度优先搜索有着密切的关系,记忆化搜索是在深度优先搜索基础上进行的优化,可以提高搜索效率和减少空间占用。选择哪一种算法取决于具体的问题和搜索场景。

需求: 一个新功能,需要在a服务中添加一个信息,把该信息的某些属性(图片)下载下来并上传到了另一个服务器中

最终使用代码

//使用sftp+用户密码方式上传
public void sshTransfer(String destinationFolderPath, String fileName, InputStream is) {

   Session session = null;
   ChannelSftp channelSftp = null;
   try {
      JSch jsch = new JSch();
      session = jsch.getSession(DESTINATION_USER, DESTINATION_HOST, DESTINATION_PORT);
      session.setPassword(DESTINATION_PASSWORD);
      session.setConfig("StrictHostKeyChecking", "no");
      session.connect(60000);

      channelSftp = (ChannelSftp) session.openChannel("sftp");
      channelSftp.connect();
      try {
         channelSftp.stat(destinationFolderPath);
      } catch (SftpException e) {
         if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
            channelSftp.mkdir(destinationFolderPath);
         } else {
            throw e;
         }
      }
      //传输文件
      channelSftp.put(is, destinationFolderPath + fileName);

   } catch (JSchException | SftpException ex) {
      ex.printStackTrace();
      throw new RuntimeException("传输文件错误: " + ex.getMessage());
   } finally {
      // 关闭sftp,关闭channel,关闭session
      if (channelSftp != null) {
         channelSftp.disconnect();
      }
      if (session != null) {
         session.disconnect();
      }
   }
}

问:

用java实现一个功能,从A服务器把一个文件写入到B服务器某个文件夹下

答:

import java.io.*;
import java.net.*;

public class FileTransfer {
   public static void main(String[] args) {
      String sourceFilePath = "/path/to/source/file.txt";
      String destinationHost = "B.hostname.com";
      String destinationFilePath = "/path/to/destination/folder/file.txt";

      try {
         File sourceFile = new File(sourceFilePath);
         FileInputStream fileInputStream = new FileInputStream(sourceFile);

         // connect to destination server
         Socket socket = new Socket(destinationHost, 8080);

         // create output stream to send file to destination server
         OutputStream outputStream = socket.getOutputStream();

         byte[] buffer = new byte[4096];
         int bytesRead = -1;

         while ((bytesRead = fileInputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
         }

         fileInputStream.close();
         outputStream.close();
         socket.close();

         System.out.println("File transfer complete.");
      } catch (IOException ex) {
         System.out.println("Error: " + ex.getMessage());
      }
   }
}

以上代码可以将指定源文件发送到B服务器的指定目录中。需要注意的是,需要在B服务器上运行一个使接收文件的服务端程序。


问:

修改当前功能,B服务器不提供接受文件的服务端程序

答:

如果B服务器不提供接收文件的服务端程序,那么可以通过SCP(Secure Copy)协议来实现文件的传输。 Java中有一个叫做JSch的库可以实现SCP协议的操作。

以下是实现这个功能的修改版代码:

import com.jcraft.jsch.*;

import java.io.*;

public class FileTransfer {
    public static void main(String[] args) {
        String sourceFilePath = "/path/to/source/file.txt";
        String destinationUser = "username";
        String destinationPassword = "password";
        String destinationHost = "B.hostname.com";
        String destinationFolderPath = "/path/to/destination/folder";

        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(destinationUser, destinationHost, 22);
            UserInfo userInfo = new UserInfo() {
                @Override
                public String getPassphrase() {
                    return null; // not used
                }

                @Override
                public String getPassword() {
                    return destinationPassword;
                }

                @Override
                public boolean promptPassword(String s) {
                    return true;
                }

                @Override
                public boolean promptPassphrase(String s) {
                    return false;
                }

                @Override
                public boolean promptYesNo(String s) {
                    return true;
                }

                @Override
                public void showMessage(String s) {
                    // not used
                }
            };
            session.setUserInfo(userInfo);
            session.connect();

            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp channelSftp = (ChannelSftp) channel;

            File sourceFile = new File(sourceFilePath);
            String fileName = sourceFile.getName();
            String destinationPath = destinationFolderPath + "/" + fileName;
            InputStream inputStream = new FileInputStream(sourceFile);

            channelSftp.put(inputStream, destinationPath);
            channelSftp.exit();
            session.disconnect();

            System.out.println("File transfer complete.");
        } catch (JSchException | FileNotFoundException | SftpException ex) {
            System.out.println("Error: " + ex.getMessage());
        }
    }
}

以上代码使用JSch库打开一个SFTP连接,并将指定源文件通过SFTP协议传输到B服务器的指定目录中。需要注意的是,需要在B服务器上安装SFTP服务端,并将相应的端口打开。


问: 是否可以使用ssh方式

答:

是可以使用SSH方式进行文件传输的。SSH是一个安全协议,具有加密和身份验证功能,因此更为安全。在Java中,可以使用JSch库来实现SSH协议操作。

以下是使用SSH方式进行文件传输的示例代码:

import com.jcraft.jsch.*;

import java.io.*;

public class FileTransfer {
    public static void main(String[] args) {
        String sourceFilePath = "/path/to/source/file.txt";
        String destinationUser = "username";
        String destinationPassword = "password";
        String destinationHost = "B.hostname.com";
        String destinationFolderPath = "/path/to/destination/folder";
        int destinationPort = 22;

        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(destinationUser, destinationHost, destinationPort);
            session.setPassword(destinationPassword);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();

            ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
            String fileName = new File(sourceFilePath).getName();
            String destinationPath = destinationFolderPath + "/" + fileName;
            String command = "scp -t " + destinationPath;
            channelExec.setCommand(command);

            OutputStream outputStream = channelExec.getOutputStream();
            InputStream inputStream = channelExec.getInputStream();

            channelExec.connect();

            if (checkAck(inputStream) != 0) {
                System.out.println("Error: failed to execute command " + command);
            }

            // send mode
            String mode = "0644";
            long size = new File(sourceFilePath).length();
            command = "C0644 " + size + " " + fileName + "\n";
            outputStream.write(command.getBytes());
            outputStream.flush();

            if (checkAck(inputStream) != 0) {
                System.out.println("Error: failed to send file mode " + mode);
            }

            // send file
            FileInputStream fileInputStream = new FileInputStream(sourceFilePath);
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fileInputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
            fileInputStream.close();

            // send end-of-file
            buffer[0] = 0;
            outputStream.write(buffer, 0, 1);
            outputStream.flush();

            if (checkAck(inputStream) != 0) {
                System.out.println("Error: failed to send file");
            }

            outputStream.close();
            channelExec.disconnect();
            session.disconnect();

            System.out.println("File transfer complete.");
        } catch (JSchException | IOException ex) {
            System.out.println("Error: " + ex.getMessage());
        }
    }
    
    private static int checkAck(InputStream inputStream) throws IOException {
        int b = inputStream.read();
        if (b == 0) return b;
        if (b == -1) return -1;

        if (b == 'E' || b == 'A' || b == 'T' || b == 'S') {
            byte[] message = new byte[1024];
            int length = inputStream.read(message, 0, message.length);
            String errorMessage = new String(message, 0, length);
            System.out.println("Error: " + errorMessage);
        }

        return b;
    }
}

以上代码使用SCP命令将指定源文件通过SSH协议传输到B服务器的指定目录中。需要注意的是,需要在B服务器上开启SSH服务,并将相应的端口打开。另外,需将StrictHostKeyChecking参数设置为no,否则会弹出是否确认主机指纹的提示框,而且进行批量操作时多次输入确认很麻烦。这种处理方式对安全有一定影响,需要对风险进行评估。


问: 代码有问题,不能连接,session.connect()异常,请检查代码

答:

非常抱歉,可能是因为代码中缺少一些必要的设置导致无法连接。以下是一个可运行的SSH文件传输实现示例,你可以参考一下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;

public class SSHFileTransfer {
    private static final String SSH_PRIVATE_KEY = "/path/to/ssh_private_key";
    private static final String SSH_USER = "username";
    private static final String SSH_PASS = "password";
    private static final String SSH_HOST = "hostname";
    private static final int SSH_PORT = 22;

    public static void main(String[] args) {
        String sourceFilePath = "/path/to/local/file";
        String destinationFolderPath = "/path/to/remote/folder";

        JSch jsch = new JSch();
        try {
            Session session = jsch.getSession(SSH_USER, SSH_HOST, SSH_PORT);

            if (SSH_PRIVATE_KEY != null) {
                jsch.addIdentity(SSH_PRIVATE_KEY);
            } else {
                session.setPassword(SSH_PASS);
            }

            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            session.connect();

            Channel channel = session.openChannel("sftp");
            channel.connect();

            ChannelSftp sftpChannel = (ChannelSftp) channel;

            File sourceFile = new File(sourceFilePath);
            sftpChannel.put(new FileInputStream(sourceFile), destinationFolderPath + "/" + sourceFile.getName());

            sftpChannel.disconnect();
            session.disconnect();
        } catch (JSchException | SftpException | IOException e) {
            e.printStackTrace();
        }
    }
}

如果你的SSH服务器需要用户名密码方式登录,那么需要设置SSH_PASS变量。如果你的SSH服务器使用公钥/私钥登录方式,那么需要设置SSH_PRIVATE_KEY变量。

另外,注意需要打开SSH服务器上的SFTP服务,否则会报错如 "Unable to initialize SftpSubsystem"。


问: java.io.IOException: End of IO Stream Read

答: 这个异常出现的原因可能是在读取InputStream时,读到了流的末尾,导致无法继续读取。

这种情况通常在读取返回的数据流时会发生,比如通过HttpURLConnection获取响应流时,如果不提前设置响应流的长度,可能会读取数据不完整,出现这个异常。

解决方法:

  1. 使用BufferedReader读取输入流,可以增加一些读取次数,减少出现异常的概率。
  2. 在创建输出流之前,先调用connection.getContentLength()方法获取响应流的长度,然后再创建对应长度的数组进行读取和写入。

例如:

URL url = new URL("http://www.example.com/example");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(5000);
urlConnection.setReadTimeout(5000);
int responseCode = urlConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
    InputStream inputStream = urlConnection.getInputStream();
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    int len;
    byte[] buffer = new byte[1024];
    while ((len = inputStream.read(buffer)) != -1) {
        byteArrayOutputStream.write(buffer, 0, len);
    }
    byte[] responseData = byteArrayOutputStream.toByteArray();
    byteArrayOutputStream.close();
    inputStream.close();
    String responseString = new String(responseData, "UTF-8");
    System.out.println(responseString);
} else {
    System.out.println("Error: " + responseCode);
}

在加上异常处理的代码后:

URL url = new URL("http://www.example.com/example");
HttpURLConnection urlConnection = null;
try {
    urlConnection = (HttpURLConnection) url.openConnection();
    urlConnection.setRequestMethod("GET");
    urlConnection.setConnectTimeout(5000);
    urlConnection.setReadTimeout(5000);
    int responseCode = urlConnection.getResponseCode();
    if (responseCode == HttpURLConnection.HTTP_OK) {
        InputStream inputStream = urlConnection.getInputStream();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        int len;
        byte[] buffer = new byte[1024];
        while ((len = inputStream.read(buffer)) != -1) {
            byteArrayOutputStream.write(buffer, 0, len);
        }
        byte[] responseData = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();
        inputStream.close();
        String responseString = new String(responseData, "UTF-8");
        System.out.println(responseString);
    } else {
        System.out.println("Error: " + responseCode);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (urlConnection != null) {
        urlConnection.disconnect();
    }
}

问:(这里已经重新提问) 用java实现一个功能,使用ssh从A服务器把一个文件写入到B服务器某个文件夹下

答: 使用Java实现从A服务器把一个文件写入到B服务器某个文件夹下,我们可以使用Jsch实现SSH连接和数据传输。具体实现步骤如下:

  1. 引入Jsch依赖:
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>
  1. 编写实现类,如下:
import com.jcraft.jsch.*;

import java.io.*;

public class SftpFileTransfer {
    public static void main(String[] args) {
        String user = "username";
        String password = "password";
        String host = "hostname";
        String sourceFilePath = "sourceFile.txt";
        String destinationFolderPath = "/path/to/destination/folder";
        int port = 22;
 
        Session session = null;
        ChannelSftp channelSftp = null;
        try {
            // 创建JSch对象
            JSch jsch = new JSch();
            // 创建session
            session = jsch.getSession(user, host, port);
            session.setPassword(password);
            // 首次登录该服务器时候,不用输入yes
            session.setConfig("StrictHostKeyChecking", "no");
            // 连接
            session.connect(60000);
            // 打开sftp通道
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            //传输文件
            channelSftp.put(sourceFilePath, destinationFolderPath);
            System.out.println("File transfered successfully to remote server.");
        } catch (JSchException | SftpException e) {
            e.printStackTrace();
        } finally {
            // 关闭sftp,关闭channel,关闭session
            if (channelSftp != null) {
                channelSftp.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
}

以上代码通过JSch实例化一个Session对象,并设置用户名、密码、主机名和端口等属性。然后通过setConfig()方法来禁用远程主机的strict key checking,以便第一次连接远程主机时不会提示yes/no。

接下来,打开SFTP通道,使用jsch的put()方法进行文件传输,其中传入的参数sourceFilePath为本地服务器上的源文件路径,destinationFolderPath为远程服务器上的目标文件夹路径。最后在finally块中关闭由JSch打开的所有通道。

请保证在本地和远程服务器上均存在该文件及目录。如需传递多个文件,则需要多次执行put()方法。


问: 上文代码提示Session.connect: java.io.IOException: End of IO Stream Read

答: 这个错误通常是由于连接被重置或者被阻止,导致读取数据流时无法继续读取数据而引起的。解决这个问题需要以下步骤:

  1. 确认服务器是否可到达:请检查目标服务器是否启动,以及IP地址和端口是否正确。

  2. 修改StrictHostKeyChecking属性:在创建session之前,设置session.setConfig("StrictHostKeyChecking","no"),将该属性设置为no,使用非交互式方式登录。

  3. 调整超时时间:有时连接超时时间设置不当会导致该问题的发生,请适当调整maxbeforeauth、connecttime等参数。

  4. 确认用户权限:请确保使用的用户名有足够的权限在目标服务器上执行操作,如创建、修改和删除文件等。

  5. 检查JSch版本:JSch库的版本不同可能导致该问题的发生,建议使用最新版本的JSch库,尽可能避免使用过时的版本。

  6. 检查防火墙设置:有时候防火墙会阻止SSH连接,将其禁用或调整防火墙设置可能会有所帮助。

如果上述方法都未能解决问题,建议查看日志或调试代码,以确定具体原因。


问: 如果目录不存在怎么办

答: 如果要在远程服务器上创建一个目录,可以使用以下代码片段:

try {
    ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
    channelSftp.connect();
    channelSftp.mkdir("/path/to/new/directory");
    System.out.println("Directory created");
    channelSftp.disconnect(); 
} catch (JSchException | SftpException e) {
    e.printStackTrace();
}

如果要在远程服务器上上传文件到一个不存在的目录中,需要同时创建目标目录。代码示例:

String remoteDirPath = "/path/to/new/directory";
String remoteFilePath = "/path/to/new/directory/newfile.txt";

try {
    ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
    sftpChannel.connect();

    // create remote directory if it does not exist
    try {
        sftpChannel.stat(remoteDirPath);
    } catch (SftpException e) {
        if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
            sftpChannel.mkdir(remoteDirPath);
        } else {
            throw e;
        }
    }

    // upload file to remote directory
    try (InputStream inputStream = new FileInputStream("localfile.txt")) {
        sftpChannel.put(inputStream, remoteFilePath);
    }

    sftpChannel.disconnect();
} catch (JSchException | IOException | SftpException e) {
    e.printStackTrace();
}

这样,在上传文件之前会先检查目录是否存在,如果不存在会创建目录。然后再把文件上传到该目录。