Spring Boot 集成 SFTP 文件上传和下载功能

您所在的位置:网站首页 jsch上传文件夹 Spring Boot 集成 SFTP 文件上传和下载功能

Spring Boot 集成 SFTP 文件上传和下载功能

2023-08-29 09:16| 来源: 网络整理| 查看: 265

文章目录 1 摘要2 核心 Maven 依赖3 核心代码3.1 核心 SFTP 上传下载工具类3.2 SFTP 附件上传配置3.3 SFTP 附件上传与下载示例 4 测试5 推荐参考资料6 Github 源码

​ ​

1 摘要

在项目中附件上传和下载是一个常用的功能。可采用的方案有三种,第一种,将附件保存至应用服务器上,这一种应对少量不重要附件可以使用,但对于大量附件或者有重要附件的,不建议使用;第二种,将附件保存至专门的文件服务器上,这一种可应对大量、重要的附件,但是需要自行架设大存储空间的服务器;第三种保存至类似阿里云的 OSS 的服务器上,这一种也可以应对大批量、重要的附件,不过租用第三方服务器也需要付费。本文将介绍第二种,在Spring Boot 项目中通过 SFTP 的方式上传和下载附件。

2 核心 Maven 依赖 ./demo-common/pom.xml com.jcraft jsch ${jsch.version}

其中 ${jsch.version} 的版本为 0.1.55

3 核心代码 3.1 核心 SFTP 上传下载工具类 ./demo-common/src/main/java/com/ljq/demo/springboot/common/util/SftpUtil.java package com.ljq.demo.springboot.common.util; import com.jcraft.jsch.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Objects; import java.util.Properties; /** * @Description: sftp 工具类 * @Author: junqiang.lu * @Date: 2020/9/23 */ public class SftpUtil { private SftpUtil() { } /** * 每个目录下最大子文件(夹)数量 */ private static final int MAX_CHILD_FILE_NUMBER = 1000; private static volatile ChannelSftp sftp = null; private static volatile Session sshSession = null; /** * 附件上传 * * @param sftpConfig sftp 配置信息 * @param fileName 文件名 * @param inputStream 文件流 * @return * @throws SftpException * @throws JSchException */ public static String upload(SftpConfig sftpConfig, String fileName, InputStream inputStream) throws SftpException, JSchException { return upload(sftpConfig,null, fileName, inputStream); } /** * 文件上传 * * @param sftpConfig sftp 配置信息 * @param relativePath 文件保存的相对路径(最后一级目录) * @param fileName 文件名 * @param inputStream 文件流 * @return * @throws JSchException * @throws SftpException */ public static String upload(SftpConfig sftpConfig, String relativePath, String fileName, InputStream inputStream) throws JSchException, SftpException { init(sftpConfig); createFolder(sftpConfig.getPath()); String filePath = sftpConfig.getPath(); if (relativePath != null && !relativePath.trim().isEmpty()) { filePath = sftpConfig.getPath() + "/" + relativePath; } filePath = generateValidPath(filePath); filePath = filePath + "/" + fileName; sftp.put(inputStream, filePath); return filePath.substring(sftpConfig.getPath().length() + 1); } /** * 文件下载 * * @param sftpConfig sftp 配置信息 * @param fileName * @return * @throws JSchException * @throws SftpException */ public static byte[] download(SftpConfig sftpConfig, String fileName) throws JSchException, SftpException, IOException { init(sftpConfig); InputStream inputStream = sftp.get(sftpConfig.getPath() + "/" + fileName); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int n; byte[] data = new byte[1024]; while ((n = inputStream.read(data, 0, data.length)) != -1) { buffer.write(data, 0, n); } buffer.flush(); return buffer.toByteArray(); } /** * 判断是否关闭 * * @return */ public static boolean isClosed() { if (Objects.isNull(sshSession) || Objects.isNull(sftp)) { return false; } if (Objects.nonNull(sshSession) && sshSession.isConnected() && Objects.nonNull(sftp) && sftp.isConnected()) { return true; } return false; } /** * 关闭 */ public static void close(){ if (sshSession != null && sshSession.isConnected()) { sshSession.disconnect(); } if (sftp != null && sftp.isConnected()) { sftp.disconnect(); } } /** * 初始化 * * @throws JSchException */ private static void init(SftpConfig sftpConfig) throws JSchException { if (!isClosed()) { synchronized (SftpUtil.class) { if (!isClosed()) { JSch jsch = new JSch(); jsch.getSession(sftpConfig.getUsername(), sftpConfig.getHost(), sftpConfig.getPort()); sshSession = jsch.getSession(sftpConfig.getUsername(), sftpConfig.getHost(), sftpConfig.getPort()); sshSession.setPassword(sftpConfig.getPassword()); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", "no"); sshSession.setConfig(sshConfig); sshSession.connect(); Channel channel = sshSession.openChannel("sftp"); channel.connect(); sftp = (ChannelSftp) channel; } } } } /** * 创建目录 * * @param path 目录路径 * @return */ private static boolean createFolder(String path) { try { sftp.mkdir(path); } catch (SftpException e) { return false; } return true; } /** * 统计目录下文件(夹)数量 * * @param path * @return */ private static int countFiles(String path) throws SftpException { sftp.cd(path); return sftp.ls(path).size(); } /** * 校验路径是否可用 * * @param path * @return */ private static boolean validatePathValid(String path) { int countFiles = 0; try { countFiles = countFiles(path); } catch (SftpException e) { createFolder(path); } if (countFiles if (validatePathValid(path)) { return path; } else { String newPath = path + String.valueOf(System.currentTimeMillis()).substring(9); createFolder(newPath); return newPath; } } /** * sftp 配置信息 */ @Data @NoArgsConstructor @AllArgsConstructor public class SftpConfig { /** * 主机地址 */ private String host; /** * sftp 连接端口 */ private int port; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 文件保存根路径 */ private String path; } public static SftpConfig sftpConfig() { return new SftpUtil().new SftpConfig(); } public static SftpConfig sftpConfig(String host, int port, String username, String password, String path) { return new SftpUtil().new SftpConfig(host, port, username, password, path); } }

3.2 SFTP 附件上传配置

SFTP 配置类

./demo-base-web/src/main/java/com/ljq/demo/springboot/baseweb/config/SftpUploadConfig.java package com.ljq.demo.springboot.baseweb.config; import lombok.Getter; import lombok.ToString; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; /** * @Description: sftp 附件上传配置 * @Author: junqiang.lu * @Date: 2020/9/27 */ @ToString @Getter @Configuration public class SftpUploadConfig { /** * 主机地址 */ @Value("${uploadSftp.host}") private String host; /** * sftp 连接端口 */ @Value("${uploadSftp.port}") private int port; /** * 用户名 */ @Value("${uploadSftp.username}") private String username; /** * 密码 */ @Value("${uploadSftp.password}") private String password; /** * 文件保存路径 */ @Value("${uploadSftp.path}") private String path; }

SFTP 配置信息

./demo-web/src/main/resources/application-dev.yml ./demo-web/src/main/resources/application-test.yml ./demo-web/src/main/resources/application-pro.yml ## sftp 附件上传配置 uploadSftp: host: 172.16.140.10 port: 22 username: root password: root path: /home/ljq/upload

3.3 SFTP 附件上传与下载示例

Service 层

Service 接口:

./demo-service/src/main/java/com/ljq/demo/springboot/service/CommonService.java package com.ljq.demo.springboot.service; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.SftpException; import com.ljq.demo.springboot.baseweb.api.ApiResult; import com.ljq.demo.springboot.baseweb.exception.ParamsCheckException; import com.ljq.demo.springboot.vo.DownloadBean; import org.springframework.http.ResponseEntity; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; /** * @Description: 公共业务 * @Author: junqiang.lu * @Date: 2018/12/24 */ public interface CommonService { /** * 通过 sftp 上传附件至固定服务器 * * @param file 文件 * @param dir 文件夹名称 * @return * @throws ParamsCheckException */ ApiResult uploadSftp(MultipartFile file, String dir) throws ParamsCheckException; /** * * 通过 sftp 下载文件 * * @param filePath 文件路径 * @return * @throws JSchException * @throws SftpException * @throws IOException */ ResponseEntity downloadSftp(String filePath) throws JSchException, SftpException, IOException; }

Service 实现类:

./demo-service/src/main/java/com/ljq/demo/springboot/service/impl/CommonServiceImpl.java package com.ljq.demo.springboot.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.util.StrUtil; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.SftpException; import com.ljq.demo.springboot.baseweb.api.ApiResult; import com.ljq.demo.springboot.baseweb.api.ResponseCode; import com.ljq.demo.springboot.baseweb.config.OSSConfig; import com.ljq.demo.springboot.baseweb.config.PDFExportConfig; import com.ljq.demo.springboot.baseweb.config.SftpUploadConfig; import com.ljq.demo.springboot.baseweb.exception.ParamsCheckException; import com.ljq.demo.springboot.baseweb.util.OSSBootUtil; import com.ljq.demo.springboot.baseweb.util.OSSSingleUtil; import com.ljq.demo.springboot.baseweb.util.PDFUtil; import com.ljq.demo.springboot.baseweb.util.ResourceFileUtil; import com.ljq.demo.springboot.common.util.SftpUtil; import com.ljq.demo.springboot.service.CommonService; import com.ljq.demo.springboot.vo.DownloadBean; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * @Description: 公共业务具体实现类 * @Author: junqiang.lu * @Date: 2018/12/24 */ @Slf4j @Service("commonService") public class CommonServiceImpl implements CommonService { @Autowired private PDFExportConfig pdfExportConfig; @Autowired private OSSConfig ossConfig; @Autowired private SftpUploadConfig uploadConfig; /** * 通过 sftp 上传附件至固定服务器 * * @param file 文件 * @param dir 文件夹名称 * @return * @throws ParamsCheckException */ @Override public ApiResult uploadSftp(MultipartFile file, String dir) throws ParamsCheckException { if (Objects.isNull(file) || file.isEmpty()) { throw new ParamsCheckException(ResponseCode.MISS_REQUEST_PART_ERROR); } log.debug("原始文件名:{},文件大小: {} Mb", file.getOriginalFilename(), (file.getSize()/1048576)); // 储存文件 SftpUtil.SftpConfig sftpConfig = SftpUtil.sftpConfig(); BeanUtil.copyProperties(uploadConfig, sftpConfig, CopyOptions.create().ignoreNullValue().ignoreError()); String filePath; if (StrUtil.isNotBlank(dir)) { dir = dir.replaceAll("[^a-zA-Z0-9]", ""); } try { filePath = SftpUtil.upload(sftpConfig,dir, System.currentTimeMillis() + file.getOriginalFilename(), file.getInputStream()); } catch (Exception e) { log.warn("文件上传失败,{}", e.getMessage()); throw new ParamsCheckException(ResponseCode.FAIL); } return ApiResult.success(filePath); } /** * 通过 sftp 下载文件 * * @param filePath 文件路径 * @return * @throws JSchException * @throws SftpException * @throws IOException */ @Override public ResponseEntity downloadSftp(String filePath) throws JSchException, SftpException, IOException { SftpUtil.SftpConfig sftpConfig = SftpUtil.sftpConfig(); BeanUtil.copyProperties(uploadConfig, sftpConfig, CopyOptions.create().ignoreNullValue().ignoreError()); byte[] bytes = SftpUtil.download(sftpConfig, filePath); HttpHeaders headers = new HttpHeaders(); String fileName = filePath.substring(filePath.lastIndexOf("/") + 1); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDispositionFormData("attachment", new String(fileName.getBytes("UTF-8"),"ISO-8859-1")); return new ResponseEntity(bytes, headers, HttpStatus.OK); } }

Controller 层:

./demo-web/src/main/java/com/ljq/demo/springboot/web/controller/CommonController.java package com.ljq.demo.springboot.web.controller; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.SftpException; import com.ljq.demo.springboot.baseweb.api.ApiResult; import com.ljq.demo.springboot.baseweb.exception.ParamsCheckException; import com.ljq.demo.springboot.common.annotation.ParamsCheck; import com.ljq.demo.springboot.service.CommonService; import com.ljq.demo.springboot.vo.DownloadBean; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; /** * @Description: 公共模块控制中心 * @Author: junqiang.lu * @Date: 2018/12/24 */ @RestController @RequestMapping("api/demo/common") public class CommonController { private static final Logger logger = LoggerFactory.getLogger(CommonController.class); @Autowired private CommonService commonService; /** * 通过 sftp 上传附件至固定服务器 * * @param file 文件 * @param dir 文件夹名称 * @return * @throws ParamsCheckException */ @PostMapping(value = "/upload/sftp", produces = {MediaType.APPLICATION_JSON_VALUE}) @ApiOperation(value = "文件上传", notes = "文件上传") public ResponseEntity uploadSftp(@RequestParam("file") MultipartFile file, String dir) throws ParamsCheckException { ApiResult apiResult = commonService.uploadSftp(file, dir); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return new ResponseEntity(apiResult, headers, HttpStatus.OK); } /** * 通过 sftp 下载文件 * * @param filePath 文件路径 * @return * @throws JSchException * @throws SftpException * @throws IOException */ @ParamsCheck(ignore = true) @GetMapping(value = "/download/sftp", produces = {MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity downloadSftp(String filePath) throws JSchException, SftpException, IOException { ResponseEntity responseEntity = commonService.downloadSftp(filePath); return responseEntity; } }

4 测试

上传:

上传

下载: 下载

5 推荐参考资料

JAVA SFTP文件上传、下载及批量下载

Java InputStream to Byte Array and ByteBuffer

Java – Write to File

6 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注. 404Code



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3