龙空技术网

SpringBoot + SFTP 实现文件上传与下载实战

程序员杰哥 2632

前言:

而今同学们对“apachecommonsftp”大致比较珍视,咱们都需要剖析一些“apachecommonsftp”的相关文章。那么小编同时在网摘上网罗了一些关于“apachecommonsftp””的相关知识,希望看官们能喜欢,咱们一起来了解一下吧!

近期在工作中需要实现文件的上传与下载,一开始打算使用一些高级的文件系统,比如:FastDFS,GlusterFS,CephFS,这些高级厉害的文件存储系统,当然博主也花了两周的时间把这三个FS都玩了一遍。个人认为FastDFS使用以及部署最简单,比较适合存储图片以及中小型文件(<500M),毕竟是国产框架(点赞);而GlusterFS和CephFS,GlusterFS部署和Java对接起来较为简单,CephFS部署很费劲,对Java使用不太友好(不太方便)。当然很大原因是博主技术不够,玩不过来。在使用这些框架之后,Leader感觉公司目前的技术储备还不够成熟,最终使用常用的SFTP实现文件上传和下载。背景介绍就到这里,接下来实战吧!

SFTP介绍

SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的加密方法,语法几乎和FTP一致。相比于FTP,SFTP更安全,但更安全带来副作用就是的效率比FTP要低些。SFTP是SSH的一部分,内部是采用SSH连接,所以在以下代码中进行文件的操作都会先cd到SFTP存放文件的根路径下。Reference:SFTP与FTP比较、浅谈SFTP与FTP。

实战

1. 相关依赖(基于SpringBoot)

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.54</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency></dependencies>

2. 相关配置

#============================================================================# SFTP Client Setting#============================================================================# 协议s ip地址s 端口s 用户名s 密码s 根路径s 密钥文件路径s 密钥的密码s s session连接超时时间s channel连接超时时间s
这里暂时没有使用到使用加密密钥的方式登陆,所以暂不填写

3. 将application.properties中配置转为一个Bean

@Getter@Setter@Component@ConfigurationProperties(ignoreUnknownFields = false, prefix = "s")public class SftpProperties { private String host; private Integer port; private String protocol; private String username; private String password; private String root; private String privateKey; private String passphrase; private String sessionStrictHostKeyChecking; private Integer sessionConnectTimeout; private Integer channelConnectedTimeout;}

4. 将上传下载文件封装成Service

FileSystemService

/** * @author jason.tang * @create 2019-03-07 13:33 * @description */public interface FileSystemService { boolean uploadFile(String targetPath, InputStream inputStream) throws Exception; boolean uploadFile(String targetPath, File file) throws Exception; File downloadFile(String targetPath) throws Exception; boolean deleteFile(String targetPath) throws Exception;}
实现类:FileSystemServiceImpl(此处省略相关上传下载代码)
/** * @author jason.tang * @create 2019-03-07 13:33 * @description */@Slf4j@Service("fileSystemService")public class FileSystemServiceImpl implements FileSystemService { @Autowired private SftpProperties config; // 设置第一次登陆的时候提示,可选值:(ask | yes | no) private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking"; /** * 创建SFTP连接 * @return * @throws Exception */ private ChannelSftp createSftp() throws Exception { JSch jsch = new JSch(); log.info("Try to connect sftp[" + config.getUsername() + "@" + config.getHost() + "], use password[" + config.getPassword() + "]"); Session session = createSession(jsch, config.getHost(), config.getUsername(), config.getPort()); session.setPassword(config.getPassword()); session.connect(config.getSessionConnectTimeout()); log.info("Session connected to {}.", config.getHost()); Channel channel = session.openChannel(config.getProtocol()); channel.connect(config.getChannelConnectedTimeout()); log.info("Channel created to {}.", config.getHost()); return (ChannelSftp) channel; } /** * 加密秘钥方式登陆 * @return */ private ChannelSftp connectByKey() throws Exception { JSch jsch = new JSch(); // 设置密钥和密码 ,支持密钥的方式登陆 if (StringUtils.isNotBlank(config.getPrivateKey())) { if (StringUtils.isNotBlank(config.getPassphrase())) { // 设置带口令的密钥 jsch.addIdentity(config.getPrivateKey(), config.getPassphrase()); } else { // 设置不带口令的密钥 jsch.addIdentity(config.getPrivateKey()); } } log.info("Try to connect sftp[" + config.getUsername() + "@" + config.getHost() + "], use private key[" + config.getPrivateKey() + "] with passphrase[" + config.getPassphrase() + "]"); Session session = createSession(jsch, config.getHost(), config.getUsername(), config.getPort()); // 设置登陆超时时间 session.connect(config.getSessionConnectTimeout()); log.info("Session connected to " + config.getHost() + "."); // 创建sftp通信通道 Channel channel = session.openChannel(config.getProtocol()); channel.connect(config.getChannelConnectedTimeout()); log.info("Channel created to " + config.getHost() + "."); return (ChannelSftp) channel; } /** * 创建session * @param jsch * @param host * @param username * @param port * @return * @throws Exception */ private Session createSession(JSch jsch, String host, String username, Integer port) throws Exception { Session session = null; if (port <= 0) { session = jsch.getSession(username, host); } else { session = jsch.getSession(username, host, port); } if (session == null) { throw new Exception(host + " session is null"); } session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, config.getSessionStrictHostKeyChecking()); return session; } /** * 关闭连接 * @param sftp */ private void disconnect(ChannelSftp sftp) { try { if (sftp != null) { if (s) { s; } else if (s) { log.info("sftp is closed already"); } if (null != s) { s; } } } catch (JSchException e) { e.printStackTrace(); } }}

5. 上传文件

5.1 将inputStream上传到指定路径下(单级或多级目录)

@Overridepublic boolean uploadFile(String targetPath, InputStream inputStream) throws Exception { ChannelSftp sftp = this.createSftp(); try { s(config.getRoot()); log.info("Change path to {}", config.getRoot()); int index = targetPath.lastIndexOf("/"); String fileDir = targetPath.substring(0, index); String fileName = targetPath.substring(index + 1); boolean dirs = this.createDirs(fileDir, sftp); if (!dirs) { log.error("Remote path error. path:{}", targetPath); throw new Exception("Upload File failure"); } s(inputStream, fileName); return true; } catch (Exception e) { log.error("Upload file failure. TargetPath: {}", targetPath, e); throw new Exception("Upload File failure"); } finally { this.disconnect(sftp); }}
5.2 创建多级目录
private boolean createDirs(String dirPath, ChannelSftp sftp) { if (dirPath != null && !dirPath.isEmpty() && sftp != null) { String[] dirs = Arrays.stream(dirPath.split("/")) .filter(StringUtils::isNotBlank) .toArray(String[]::new); for (String dir : dirs) { try { s; log.info("Change directory {}", dir); } catch (Exception e) { try { s; log.info("Create directory {}", dir); } catch (SftpException e1) { log.error("Create directory failure, directory:{}", dir, e1); e1.printStackTrace(); } try { s; log.info("Change directory {}", dir); } catch (SftpException e1) { log.error("Change directory failure, directory:{}", dir, e1); e1.printStackTrace(); } } } return true; } return false;}
5.3 将文件上传到指定目录
@Overridepublic boolean uploadFile(String targetPath, File file) throws Exception { return this.uploadFile(targetPath, new FileInputStream(file));}

6. 下载文件

@Overridepublic File downloadFile(String targetPath) throws Exception { ChannelSftp sftp = this.createSftp(); OutputStream outputStream = null; try { s(config.getRoot()); log.info("Change path to {}", config.getRoot()); File file = new File(targetPath.substring(targetPath.lastIndexOf("/") + 1)); outputStream = new FileOutputStream(file); s(targetPath, outputStream); log.info("Download file success. TargetPath: {}", targetPath); return file; } catch (Exception e) { log.error("Download file failure. TargetPath: {}", targetPath, e); throw new Exception("Download File failure"); } finally { if (outputStream != null) { outputStream.close(); } this.disconnect(sftp); }}

7. 删除文件

/** * 删除文件 * @param targetPath * @return * @throws Exception */@Overridepublic boolean deleteFile(String targetPath) throws Exception { ChannelSftp sftp = null; try { sftp = this.createSftp(); s(config.getRoot()); s; return true; } catch (Exception e) { log.error("Delete file failure. TargetPath: {}", targetPath, e); throw new Exception("Delete File failure"); } finally { this.disconnect(sftp); }}

8. 最后

涉及到对文件的操作,一定记得将流关闭。在使用中比如下载文件,请将生成的文件在使用后删除(file.delete()),避免在服务器中占据大量资源。application.proerties中SFTP相关配置,请自行更换。如有不对之处,请指出,感谢阅读!

标签: #apachecommonsftp