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: sftp
  host: 192.168.1.10
  port: 22
  username: root
  password: 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 {

    @Resource
    private 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 {

    @Resource
    private SftpUtils sftpUtils;

    @Override
    public 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 {
            // 关闭sftp
            sftpUtils.disconnect(sftp);
        }
    }

    @Override
    public 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 {
            // 关闭sftp
            sftpUtils.disconnect(sftp);
        }
    }

    @Override
    public 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 {
            // 关闭sftp
            sftpUtils.disconnect(sftp);
        }
    }

    @Override
    public 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 {
            // 关闭sftp
            sftpUtils.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 {

    @Resource
    private 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.mfbz.cn/a/364608.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go并发编程-介绍与Goroutine使用

1. 并发介绍 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中的多个…

ElementUI 组件:Layout布局(el-row、el-col)

ElementUI安装与使用指南 Layout布局 点击下载learnelementuispringboot项目源码 效果图 el-row_el-col.vue页面效果图 项目里el-row_el-col.vue代码 <script> export default {name:el-row_el-col 布局 }</script><template><div class"roo…

【Vue】2-14、插槽 自定义指令

一、插槽 插槽&#xff08;Slot&#xff09;是 vue 为组件的封装者提供的能力。允许封装者在封装组件时&#xff0c;把不确定的&#xff0c;希望由用户指定的部分定义为插槽。 <template><div class"app-container"><h1>App 根组件</h1>&…

Java打印图形 九九乘法表

目录 双重循环九九乘法表打印长方形打印平行四边形打印三角形打印菱形打印空心菱形 三重循坏百钱买百鸡 双重循环 九九乘法表 在Java中&#xff0c;你可以使用嵌套的for循环来打印九九乘法表。以下是一个简单的示例&#xff1a; public class Main {public static void main…

Qt实现类似ToDesk顶层窗口 不规则按钮

先看效果&#xff1a; 在进行多进程开发时&#xff0c;可能会遇到需要进行全局弹窗的需求。 因为平时会使用ToDesk进行远程桌面控制&#xff0c;在电脑被控时&#xff0c;ToDesk会在右下角进行一个顶层窗口的提示&#xff0c;效果如下&#xff1a; 其实要实现顶层窗口&#xf…

sql指南之null值用法

注明&#xff1a;参考文章&#xff1a; SQL避坑指南之NULL值知多少&#xff1f;_select null as-CSDN博客文章浏览阅读2.9k次&#xff0c;点赞7次&#xff0c;收藏21次。0 引言 SQL NULL&#xff08;UNKNOW&#xff09;是用来代表缺失值的术语&#xff0c;在表中的NULL值是显示…

python打造光斑处理系统5:查看光强分布

文章目录 光斑分布峰值和均值三维分布 光斑处理&#xff1a;python处理高斯光束的图像 光斑处理系统&#xff1a;程序框架&#x1f31f;打开图像&#x1f31f;参数对话框/伪彩映射&#x1f31f;裁切ROI 光斑分布 光斑作为图像而言&#xff0c;其表现能力是有限的&#xff0c;…

Springboot-前后端分离——第三篇(三层架构与控制反转(IOC)-依赖注入(DI)的学习)

本篇主要对ControllerServiceDAO三层结构以及控制反转&#xff08;IOC&#xff09;与DI&#xff08;依赖注入&#xff09;进行总结。 目录 一、三层架构&#xff1a; Controller/Service/DAO简介&#xff1a; 二、控制反转(IOC)-依赖注入(DI): 概念介绍&#xff1a; DOC与…

EtherCAT FP介绍系列文章—UDP gateway

EtherCAT主站上的Mailbox Gateway功能&#xff0c;可以用于将EtherCAT mailbox相关协议从外部设备的工具通过邮箱网关路由到EtherCAT从站设备。在EtherCAT规范中定义的所有邮箱协议在此功能中都可用&#xff0c;例如CoE, FoE, VoE, SoE。 但是&#xff0c;这里特别注意的是Mai…

Autonomous_Exploration_Development_Environment的local_planner学习笔记

1.程序下载网址&#xff1a;https://github.com/HongbiaoZ/autonomous_exploration_development_environment 2.相关参考资料&#xff1a; https://blog.csdn.net/lizjiwei/article/details/124437157 Matlab用采样的离散点做前向模拟三次样条生成路径点-CSDN博客 CMU团队开…

Hbase-2.4.11_hadoop-3.1.3集群_大数据集群_SSH修改默认端口22为其他端口---记录025_大数据工作笔记0185

其实修改起来非常简单,但是在大数据集群中,使用到了很多的脚步,也需要修改, 这里把,大数据集群,整体如何修改SSH端口,为22022,进行总结一下: 0.hbase-2.4.11的话,hbase集群修改默认SSH端口22,修改成22022,需要修改 需要修改/opt/module/hbase-2.4.11/conf/hbase-env.sh 这里…

RocketMQ—RocketMQ快速入门

RocketMQ—RocketMQ快速入门 RocketMQ提供了发送多种发送消息的模式&#xff0c;例如同步消息&#xff0c;异步消息&#xff0c;顺序消息&#xff0c;延迟消息&#xff0c;事务消息等。 消息发送和监听的流程 消息生产者 创建消息生产者producer&#xff0c;并制定生产者组…

element-ui button 仿写 demo

基于上篇 button 源码分享写了一个简单 demo&#xff0c;在写 demo 的过程中&#xff0c;又发现了一个小细节&#xff0c;分享一下&#xff1a; 1、组件部分&#xff1a; <template><buttonclass"yss-button"click"handleClick":class"[ty…

基于python+django,我开发了一款药店信息管理系统

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 功能包括&#xff1a;药品管理、分类管理、顾客管理、用户管理、日志管理、系统信息模块。 代码结构 server目录是后端代码web目录是前端代码 部署运行…

Java的JVM学习一

一、java中的内存结构如何划分 栈和堆的区别&#xff1a; 栈负责处理运行&#xff0c;堆负债处理存储。 区域名称作用虚拟机栈用于存储正在执行的每个Java方法&#xff0c;以及其方法的局部变量表等。局部变量表存放了便器可知长度的各种基本数据类型&#xff0c;对象引用&am…

jmerter-01安装与界面介绍

文章目录 jmeter安装 jmeter安装 1.配置JDK环境 Jmeter到目前为止只支持java 8 2.解压JMeter安装包 就可以双击jmeter.bat 运行启动 3.运行过程中&#xff0c;不要关掉小黑窗 这个黑框不要关闭 jmeter图示

小程序:类型三级分类

一、效果图片 二、代码 <template><view class"customPosition"><!-- header --><navBar :border"false" :hasBack"true" :title"titleName"></navBar><!-- 查询 --><view class"search…

极易录用!并行结构!GAF-PCNN-AT格拉姆角场和双通道PCNN融合注意力机制的分类/故障识别程序!Excel导入,直接运行

适用平台&#xff1a;Matlab2023版本及以上 本程序参考中文EI期刊《电力自动化设备》12月29号网络首发文献&#xff1a;《基于格拉姆角场与并行CNN的并网逆变器开关管健康诊断》,此外&#xff0c;在此基础上进一步对模型进行复现并改进&#xff0c;该论文推出时间短&#xff0…

HTML -- 常用标签

标签 表示HTML网页内容的一个最基本的组织单元&#xff0c;类似于语文中的标点符号&#xff0c; 标签的作用&#xff1a;告诉浏览器当前标签中的内容是什么&#xff0c;以什么格式在页面中进行呈现 单标签 单标签&#xff08;只有一个标签名的标签&#xff09;的标签格式&…

Open CASCADE学习|球面上曲线长度计算

球和球面是数学和物理学中非常重要的概念&#xff0c;它们在许多领域都有广泛的应用。 球面是指所有与固定点等距离的点的集合&#xff0c;这个固定点被称为球心&#xff0c;而这个等距离的长度就是球的半径。球面是一个二维曲面&#xff0c;它是三维空间中点与距离之间关系的…
最新文章