SpringBoot实战(二十六)集成SFTP

目录

    • 一、SFTP简介
    • 二、SpringBoot 集成
      • 2.1 Maven 依赖
      • 2.2 application.yml 配置
      • 2.3 DemoController.java 接口
      • 2.4 SftpService.java
      • 2.5 DemoServiceImpl.java 实现类
      • 2.6 SftpUtils.java 工具类
      • 2.7 执行结果
        • 1)上传文件
        • 2)下载文件
        • 3)重命名文件(移动)
        • 4)删除文件

一、SFTP简介

SFTP:全称 Secure File Transfer Protocol,是一种安全文件传输协议,它基于 SSH(Secure Shell)协议并为其提供了文件传输服务。相比于传统的 FTP,SFTP 提供了加密的数据传输通道,能够有效保护数据在传输过程中不被窃取和篡改,增强了安全性。

  • SFTP 服务器,在 LinuxMac 系统中是自带的,可以直接使用 Linux 的用户名/密码来登录 SFTP,windows 下默认只支持 SFTP 客户端,不支持 SFTP 服务器。

SFTP 登录命令:

# 和ssh命令一样:用户名@ip
sftp root@192.168.1.123

补充:由于 Linux 默认集成了 SFTP 服务器,所以测试 SpringBoot 集成 SFTP 的时候不需要再搭建 SFTP,直接用户名/密码连 Linux 系统即可。


二、SpringBoot 集成

2.1 Maven 依赖

<!-- SFTP -->
<dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version>
</dependency>

2.2 application.yml 配置

sftp:protocol: sftphost: 192.168.1.10port: 22username: rootpassword: root

2.3 DemoController.java 接口

import com.demo.common.Result;
import com.demo.service.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;/*** <p> @Title DemoController* <p> @Description 测试Controller** @author ACGkaka* @date 2023/4/24 18:02*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {@Resourceprivate DemoService demoService;/*** 上传文件*/@PostMapping("/upload")public Result<Object> upload(@RequestParam String sftpPath, @RequestParam MultipartFile file) {demoService.upload(sftpPath, file);return Result.succeed();}/*** 下载文件*/@GetMapping("/download")public void download(@RequestParam String sftpPath,  HttpServletResponse response) {demoService.download(sftpPath, response);}/*** 重命名文件(移动)*/@GetMapping("/rename")public Result<Object> rename(@RequestParam String oldPath,  @RequestParam String newPath) {demoService.rename(oldPath, newPath);return Result.succeed();}/*** 删除文件*/@GetMapping("/delete")public Result<Object> delete(@RequestParam String sftpPath) {demoService.delete(sftpPath);return Result.succeed();}
}

2.4 SftpService.java

import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;/*** <p> @Title DemoService* <p> @Description 测试Service** @author ACGkaka* @date 2023/4/24 18:13*/
public interface DemoService {/*** 上传文件* @param sftpPath 路径* @param file 文件*/void upload(String sftpPath, MultipartFile file);/*** 下载文件* @param sftpPath* @param response*/void download(String sftpPath, HttpServletResponse response);/*** 重命名文件(移动)* @param oldPath* @param newPath*/void rename(String oldPath, String newPath);/*** 删除文件* @param sftpPath*/void delete(String sftpPath);
}

2.5 DemoServiceImpl.java 实现类

import com.demo.service.DemoService;
import com.demo.util.SftpUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;/*** <p> @Title DemoServiceImpl* <p> @Description 测试ServiceImpl** @author ACGkaka* @date 2023/4/24 18:14*/
@Slf4j
@Service
public class DemoServiceImpl implements DemoService {@Resourceprivate SftpUtils sftpUtils;@Overridepublic void upload(String sftpPath, MultipartFile file) {// 上传文件ChannelSftp sftp = null;try (InputStream in = file.getInputStream()) {// 开启sftp连接sftp = sftpUtils.createSftp();// 进入sftp文件目录sftp.cd(sftpPath);log.info("修改目录为:{}", sftpPath);// 上传文件sftp.put(in, file.getOriginalFilename());log.info("上传文件成功,目标目录:{}", sftpPath);} catch (SftpException | JSchException | IOException e) {log.error("上传文件失败,原因:{}", e.getMessage(), e);throw new RuntimeException("上传文件失败");} finally {// 关闭sftpsftpUtils.disconnect(sftp);}}@Overridepublic void download(String sftpPath, HttpServletResponse response) {// 下载文件long start = System.currentTimeMillis();ChannelSftp sftp = null;try {// 开启sftp连接sftp = sftpUtils.createSftp();// 判断sftp文件存在File sftpFile = new File(sftpPath);boolean isExist = isFileExist(sftpPath, sftp);if (isExist) {// 下载文件response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(sftpFile.getName(), "utf-8"));sftp.get(sftpFile.getName(), response.getOutputStream());// 记录日志long time = System.currentTimeMillis() - start;log.info("sftp文件下载成功,目标文件:{},总耗时:{}ms.", sftpPath, time);} else {log.error("sftp文件下载失败,sftp文件不存在:" + sftpFile.getParent());throw new RuntimeException("sftp文件下载失败,sftp文件不存在:" + sftpFile.getParent());}} catch (SftpException | JSchException | IOException e) {log.error("sftp文件下载失败,目标文件名:{},原因:{}", sftpPath, e.getMessage(), e);throw new RuntimeException("sftp文件下载失败");} finally {// 关闭sftpsftpUtils.disconnect(sftp);}}@Overridepublic void rename(String oldPath, String newPath) {// 重命名文件(移动)ChannelSftp sftp = null;try {// 开启sftp连接sftp = sftpUtils.createSftp();// 修改sftp文件路径sftp.rename(oldPath, newPath);log.info("sftp文件重命名成功,历史路径:{},新路径:{}", oldPath, newPath);} catch (SftpException | JSchException e) {log.error("sftp文件重命名失败,原因:{}", e.getMessage(), e);throw new RuntimeException("sftp文件重命名失败");} finally {// 关闭sftpsftpUtils.disconnect(sftp);}}@Overridepublic void delete(String sftpPath) {// 删除文件ChannelSftp sftp = null;try {// 开启sftp连接sftp = sftpUtils.createSftp();// 判断sftp文件存在boolean isExist = isFileExist(sftpPath, sftp);if (isExist) {// 删除文件SftpATTRS sftpATTRS = sftp.lstat(sftpPath);if (sftpATTRS.isDir()) {sftp.rmdir(sftpPath);} else {sftp.rm(sftpPath);}log.info("sftp文件删除成功,目标文件:{}.", sftpPath);} else {log.error("sftp文件删除失败,sftp文件不存在:" + sftpPath);throw new RuntimeException("sftp文件删除失败,sftp文件不存在:" + sftpPath);}} catch (SftpException | JSchException e) {log.error("sftp文件删除失败,原因:{}", e.getMessage(), e);throw new RuntimeException("sftp文件删除失败");} finally {// 关闭sftpsftpUtils.disconnect(sftp);}}/*** 判断目录是否存在*/private boolean isFileExist(String sftpPath, ChannelSftp sftp) {try {// 获取文件信息SftpATTRS sftpATTRS = sftp.lstat(sftpPath);return sftpATTRS != null;} catch (Exception e) {log.error("判断文件是否存在失败,原因:{}", e.getMessage(), e);return false;}}
}

2.6 SftpUtils.java 工具类

import com.demo.config.SftpProperties;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** <p> @Title SftpUtils* <p> @Description SFTP工具类** @author ACGkaka* @date 2024/1/31 17:41*/
@Slf4j
@Component
public class SftpUtils {@Resourceprivate SftpProperties sftpProperties;/*** 创建SFTP连接*/public ChannelSftp createSftp() throws JSchException {JSch jsch = new JSch();log.info("Try to connect sftp[" + sftpProperties.getUsername() + "@" + sftpProperties.getHost() + "]");Session session = createSession(jsch, sftpProperties.getHost(), sftpProperties.getUsername(), sftpProperties.getPort());session.setPassword(sftpProperties.getPassword());session.setConfig("StrictHostKeyChecking", "no");// 默认情况下,JSch库本身并没有会话超时时间。// 为了避免长时间无活动连接占用资源或因网络问题导致连接挂起而不被释放,通常建议设置会话超时,(单位:毫秒)session.setTimeout(30000);session.connect();log.info("Session connected to {}.", sftpProperties.getHost());Channel channel = session.openChannel(sftpProperties.getProtocol());channel.connect();log.info("Channel created to {}.", sftpProperties.getHost());return (ChannelSftp) channel;}/*** 创建 Session*/public Session createSession(JSch jsch, String host, String username, Integer port) throws JSchException {Session session = null;if (port <= 0) {session = jsch.getSession(username, host);} else {session = jsch.getSession(username, host, port);}if (session == null) {throw new RuntimeException(host + "session is null");}return session;}/*** 关闭连接*/public void disconnect(ChannelSftp sftp) {try {if (sftp != null) {if (sftp.isConnected()) {sftp.disconnect();} else if (sftp.isClosed()) {log.error("sftp 连接已关闭");}if (sftp.getSession() != null) {sftp.getSession().disconnect();}}} catch (JSchException e) {log.error("sftp 断开连接失败,原因:{}", e.getMessage(), e);}}
}

2.7 执行结果

1)上传文件

请求地址:http://localhost:8080/demo/upload

执行结果:

在这里插入图片描述

2)下载文件

请求地址:http://localhost:8080/demo/download?sftpPath=/home/root/test.pdf

下载后,文件可以正常打开:

在这里插入图片描述

3)重命名文件(移动)

请求地址:http://localhost:8080/demo/rename?oldPath=/home/root/test1.pdf&newPath=/home/root/test/test.pdf

执行结果:

在这里插入图片描述

可以去sftp上面看下,文件已经被移动了:

在这里插入图片描述

4)删除文件

请求地址:http://localhost:8080/demo/delete?sftpPath=/home/root/test/test1.pdf

执行结果:

在这里插入图片描述

可以去sftp上面看下,文件已经被成功删除了:

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.SFTP命令用法(上传和下载 ),https://blog.csdn.net/JacaCao/article/details/108190174

2.springBoot整合sftp,https://blog.csdn.net/winsanity/article/details/120665642

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/444765.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kotlin 协程1:深入理解withContext

Kotlin 协程1&#xff1a;深入理解withContext 引言 在现代编程中&#xff0c;异步编程已经变得非常重要。在 Kotlin 中&#xff0c;协程提供了一种优雅和高效的方式来处理异步编程和并发。在这篇文章中&#xff0c;我们将深入探讨 Kotlin 协程中的一个重要函数&#xff1a;wi…

物联网可视化平台:赋能企业数字化转型

在数字化转型的大潮中&#xff0c;企业面临着如何更好地理解和利用海量数据的挑战。物联网技术的快速发展&#xff0c;为企业提供了一个全新的视角和解决方案。通过物联网可视化平台&#xff0c;企业能够实时监控、分析和展示物联网数据&#xff0c;从而加速数字化转型的进程。…

RabbitMQ 死信队列应用

1. 概念 死信队列&#xff08;Dead Letter Queue&#xff09;是在消息队列系统中的一种特殊队列&#xff0c;用于存储无法被消费的消息。消息可能会因为多种原因变成“死信”&#xff0c;例如消息过期、消息被拒绝、消息队列长度超过限制等。当消息变成“死信”时&#xff0c;…

《高性能MySQL》

文章目录 一、创建1. 磁盘1.1 页、扇区、寻道、寻址、硬盘性能 2. 行结构row_format2.1 Compact紧凑2.1.1 行溢出2.1.2 作用2.1.3 内容1-额外信息1、变长字段长度2、NULL值列表3、记录头信息 2.1.4 内容2-真实数据4、表中列的值5、transaction_id6、roll_point7、row_id 2.2 dy…

MD5算法:高效安全的数据完整性保障

摘要&#xff1a;在数字世界中&#xff0c;确保数据完整性和安全性至关重要。消息摘要算法就是一种用于实现这一目标的常用技术。其中&#xff0c;Message Digest Algorithm 5&#xff08;MD5&#xff09;算法因其高效性和安全性而受到广泛关注。本文将详细介绍MD5算法的优缺点…

ElementUI 组件:Container 布局容器实例

ElementUI安装与使用指南 Container 布局容器 点击下载learnelementuispringboot项目源码 效果图 el-container-example.vue&#xff08;Container 布局容器实例&#xff09;页面效果图 项目里el-container-example.vue代码 <script> export default {name: el_cont…

<网络安全>《12 数据库安全审计系统》

1 概念 数据库安全审计系统通过对用户访问数据库行为的记录、分析和汇报&#xff0c;来帮助用户事后生成合规报告、事故追根溯源&#xff0c;同时通过大数据搜索技术提供高效查询审计报告&#xff0c;定位事件原因&#xff0c;以便日后查询、分析、过滤&#xff0c;实现加强内…

SpringBoot使用Rabbit详解含完整代码

1. 摘要 本文将详细介绍如何在Spring Boot应用程序中集成和使用RabbitMQ消息队列。RabbitMQ是一个开源的消息代理和队列服务器&#xff0c;用于通过轻量级和可靠的消息在应用程序或系统之间进行异步通信。本文将通过步骤说明、代码示例和详细注释&#xff0c;指导读者在Spring…

代码重构的招式

背景介绍 最近在团队工作中花了不少心思主导建设了测试平台&#xff0c;前期的建设思路是能用就行&#xff0c;随着建设的深入&#xff0c;逐渐需要学习下代码架构设计方面的内容了。于是参加了公司组织的代码重构与模式的培训&#xff0c;通过培训&#xff0c;感觉收获颇丰&a…

往年国自然项目信息查看

1 国自然申报系统 进去可以看到摘要。 2 letpub

(1)从 AGP 4.1.2 升级到 7.5.1 我遇到了什么问题

AGP 升级问题 &#xff08;1&#xff09;Could not get unknown property ‘project’ for settings&#xff0c;on project.buildscript 问题 Could not get unknown property ‘project’ for settings ‘AGP1’ of type org.gradle.initialization.DefaultSettings. agp4 …

备战蓝桥杯---数据结构与STL应用(入门4)

本专题主要是关于利用优先队列解决贪心选择上的“反悔”问题 话不多说&#xff0c;直接看题&#xff1a; 下面为分析&#xff1a; 很显然&#xff0c;我们在整体上以s[i]为基准&#xff0c;先把士兵按s[i]排好。然后&#xff0c;我们先求s[i]大的开始&#xff0c;即规定选人数…