用commons vfs 框架 替换具体的sftp 实现
前提:就像接口与实现 我们需要一套标准的逻辑处理方式 而不需要关心具体是sftp 还是 ftp 还是 http 等其他具体的实现
package com.files;import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.vfs2.*; import org.apache.commons.vfs2.auth.StaticUserAuthenticator; import org.apache.commons.vfs2.impl.DefaultFileSystemConfigBuilder; import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream;public class VFSSftpTools implements AutoCloseable {private static final String FILE_URL = "sftp://%s/%s";private final FileSystemManager fsManager;private final String host;private final FileSystemOptions opts;/*** 构造函数.* @param host 主机地址* @param username 用户名* @param password 密码*/public VFSSftpTools(final String host, final String username, final String password) {try {this.host = host;opts = new FileSystemOptions();SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);final UserAuthenticator auth = new StaticUserAuthenticator(null, username, password);DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(opts, auth);fsManager = VFS.getManager();} catch (final FileSystemException e) {throw new RuntimeException(e);}}/*** vfs sftp 上传.* @param file 待上传文件* @param remotePath 远程地址*/public void upload(final File file, final String remotePath) {try (final InputStream is = FileUtils.openInputStream(file)) {uploadStream(is, remotePath);} catch (IOException e) {throw new RuntimeException(e);}}/*** vfs sftp 上传.* @param is 待上传文件流* @param remotePath 远程地址*/public void uploadStream(final InputStream is, final String remotePath) {try (final OutputStream os = fsManager.resolveFile(String.format(FILE_URL, host, remotePath), opts).getContent().getOutputStream()) {IOUtils.copy(is, os);} catch (final IOException e) {throw new RuntimeException(e);}}/*** vfs sftp 下载.* @param remotePath 远程地址* @param localPath 本地地址*/public void download(final String remotePath, final String localPath) {try (final InputStream is = fsManager.resolveFile(String.format(FILE_URL, host, remotePath), opts).getContent().getInputStream();final OutputStream os = FileUtils.openOutputStream(new File(localPath))) {IOUtils.copy(is, os);} catch (final IOException e) {throw new RuntimeException(e);}}@Overridepublic void close() {VFS.close();} } |
test
package com.sftp;import com.files.VFSSftpTools; import org.junit.Test; import org.springframework.stereotype.Component;import java.io.File;/*** 测试类。** @author test*/ @Component public class VFSSftpTest {@Testpublic void sftp() {try (final VFSSftpTools sftpTools = new VFSSftpTools("192.168.31.201:22", "test", "test")){final File file = new File("D:\\test.jar");final String remotePath = "newFolder/" + file.getName();sftpTools.upload(file, remotePath);sftpTools.download(remotePath, "D:\\test.jar");}} } |
1、FileSystemManager 是线程安全的,所以如果host一致 在循环外创建
注意:
关闭FileSystemManager的时候 有个坑, 这个坑就是通过VFS获取FileSystemManager时,VFS自己处理的单例,上传完毕释放资源不仅仅需要调用FileSystemManager的close方法 ,还需要设置FileSystemManager = null; 这样下次获取才会创建新的连接(否则我们拿到的时候一个关闭的连接)
VFS也提供了一个close 方法, 里面处理了 FileSystemManager = null; 我们需要调用这个,这个需要注意。