SpringBoot-集成FTP(上传、下载、删除)

目录

一、引入依赖

二、配置文件

三、Controller层

四、Service层

五、相关工具类


由于服务在内网部署,需要使用ftp服务器管理文件,总结如下

一、引入依赖

<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.9.0</version>
</dependency><!-- hutool -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.22</version>
</dependency>

Tip:使用commons-net 3.9.0版本,之前的版本有漏洞

二、配置文件

ftp:basePath: /host: 192.168.1.100httpPath: ftp://192.168.1.100password: demoport: 21username: demo

配置文件类:

package com.example.demo.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;/*** @Author: meng* @Description: ftp配置* @Date: 2023/6/30 13:44* @Version: 1.0*/
@Data
@Component
@ConfigurationProperties(prefix = "ftp")
public class FtpProperties {/*** ftp服务器的地址*/private String host;/*** ftp服务器的端口号(连接端口号)*/private String port;/*** ftp的用户名*/private String username;/*** ftp的密码*/private String password;/*** ftp上传的根目录*/private String basePath;/*** 回显地址*/private String httpPath;
}

三、Controller层

Tip:Response为通用返回类,不熟悉的可以看这篇文章:Springboot - 通用返回类BaseResults_W_Meng_H的博客-CSDN博客BaseResults类public class BaseResults { private Integer code; private String message; private T data; public BaseResults() { super(); } public BaseResu...https://blog.csdn.net/W_Meng_H/article/details/104995823

package com.example.demo.controller;import com.example.demo.service.FtpService;
import com.template.common.core.data.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @Author: meng* @Description: 文件服务* @Date: 2023/6/30 14:33* @Version: 1.0*/
@RestController
@RequestMapping("/file")
public class FileController {@Autowiredprivate FtpService ftpService;// 单上传文件@PostMapping(value = "upload", consumes = "multipart/*", headers = "content-type=multipart/form-data")public Response uploadFile(@RequestParam("file") MultipartFile file, HttpServletRequest request) {return ftpService.uploadMultipartFile(file, request);}// 导出文件@GetMapping(value = "download")public void downloadFile(@RequestParam String fileName, @RequestParam String ftpFilePath, HttpServletResponse response) {ftpService.downloadFileToFtp(fileName, ftpFilePath, response);}// 删除文件@GetMapping(value = "delete")public Response deleteFile(@RequestParam String ftpFilePath) {return ftpService.deleteFileToFtp(ftpFilePath);}}

四、Service层

package com.example.demo.service;import com.template.common.core.data.Response;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @Author: meng* @Description: ftp服务类* @Date: 2023/6/30 13:46* @Version: 1.0*/
public interface FtpService {/*** 上传文件到ftp* @param multipartFile* @param request* @return*/public Response uploadMultipartFile(MultipartFile multipartFile, HttpServletRequest request);/*** 下载ftp文件,直接转到输出流* @param fileName* @param ftpFilePath* @param response*/public void downloadFileToFtp(String fileName, String ftpFilePath, HttpServletResponse response);/*** 删除ftp文件* @param ftpFilePath ftp下文件路径,根目录开始* @return*/Response deleteFileToFtp(String ftpFilePath);
}

实现类:

package com.example.demo.service.impl;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.example.demo.config.FtpProperties;
import com.example.demo.service.FtpService;
import com.example.demo.tools.FileUtils;
import com.template.common.core.data.Response;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;/*** @Author: meng* @Description: ftp服务类* @Date: 2023/6/30 13:47* @Version: 1.0*/
@Service
public class FtpServiceImpl implements FtpService {private Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate FtpProperties ftpProperties;@Overridepublic Response uploadMultipartFile(MultipartFile multipartFile, HttpServletRequest request) {if (multipartFile == null || multipartFile.isEmpty()) {return Response.error("没有上传文件");}//1、获取原文件后缀名String originalFileName = multipartFile.getOriginalFilename();String suffix = originalFileName.substring(originalFileName.lastIndexOf('.'));//2、使用UUID生成新文件名String newFileName = IdUtil.fastSimpleUUID();//3、将MultipartFile转化为FileFile file = null;try {file = FileUtils.multipartFileToFile(multipartFile);} catch (Exception e) {logger.error("文件上传失败:", e);return Response.error("文件上传失败");}if (ObjectUtil.isNull(file)) {return Response.error("文件上传失败");}//当前日期字符串String today = File.separator + DateUtil.format(new Date(), "yyyyMMdd");Boolean flag = uploadFileToFtp(file, newFileName + suffix, today);if (!flag) {return Response.error("文件上传失败");}System.out.println(file.toURI());File f = new File(file.toURI());f.delete();String filePath = ftpProperties.getHttpPath() + today + File.separator + newFileName + suffix;return Response.success(filePath);}public Boolean uploadFileToFtp(File file, String fileName, String filePath) {logger.info("调用文件上传接口");// 定义保存结果boolean iaOk = false;// 初始化连接FTPClient ftp = connectFtpServer();if (ftp != null) {try {// 设置文件传输模式为二进制,可以保证传输的内容不会被改变ftp.setFileType(FTP.BINARY_FILE_TYPE);ftp.enterLocalPassiveMode();    //注:上传文件都为0字节,设置为被动模式即可// 跳转到指定路径,逐级跳转,不存在的话就创建再进入toPathOrCreateDir(ftp, filePath);InputStream inputStream = new FileInputStream(file);// 上传文件 参数:上传后的文件名,输入流,,返回Boolean类型,上传成功返回trueiaOk = ftp.storeFile(fileName, inputStream);// 关闭输入流inputStream.close();// 退出ftpftp.logout();} catch (IOException e) {logger.error(e.toString());} finally {if (ftp.isConnected()) {try {// 断开ftp的连接ftp.disconnect();} catch (IOException ioe) {logger.error(ioe.toString());}}}}return iaOk;}@Overridepublic void downloadFileToFtp(String fileName, String ftpFilePath, HttpServletResponse response) {FTPClient ftpClient = connectFtpServer();try {ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);ftpClient.enterLocalPassiveMode();// 设置文件ContentType类型,这样设置,会自动判断下载文件类型response.setContentType("application/x-msdownload");// 设置文件头:最后一个参数是设置下载的文件名并编码为UTF-8response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));InputStream is = ftpClient.retrieveFileStream(ftpFilePath);BufferedInputStream bis = new BufferedInputStream(is);OutputStream out = response.getOutputStream();byte[] buf = new byte[1024];int len = 0;while ((len = bis.read(buf)) > 0) {out.write(buf, 0, len);}out.flush();out.close();if (bis != null) {try {bis.close();} catch (IOException e) {e.printStackTrace();}}if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}ftpClient.logout();} catch (Exception e) {logger.error("FTP文件下载失败:", e);} finally {if (ftpClient.isConnected()) {try {ftpClient.disconnect();} catch (IOException ioe) {logger.error(ioe.toString());}}}}@Overridepublic Response deleteFileToFtp(String ftpFilePath) {FTPClient ftp = connectFtpServer();boolean resu = false;try {resu = ftp.deleteFile(ftpFilePath);ftp.logout();} catch (Exception e) {logger.error("FTP文件删除失败:{}", e);return Response.error("文件删除失败");} finally {if (ftp.isConnected()) {try {ftp.disconnect();} catch (IOException ioe) {logger.error(ioe.toString());}}}return Response.SUCCESS;}private FTPClient connectFtpServer() {// 创建FTPClient对象(对于连接ftp服务器,以及上传和上传都必须要用到一个对象)logger.info("创建FTP连接");FTPClient ftpClient = new FTPClient();// 设置连接超时时间ftpClient.setConnectTimeout(1000 * 60);// 设置ftp字符集ftpClient.setControlEncoding("utf-8");// 设置被动模式,文件传输端口设置,否则文件上传不成功,也不报错ftpClient.enterLocalPassiveMode();try {// 定义返回的状态码int replyCode;// 连接ftp(当前项目所部署的服务器和ftp服务器之间可以相互通讯,表示连接成功)ftpClient.connect(ftpProperties.getHost());// 输入账号和密码进行登录ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());// 接受状态码(如果成功,返回230,如果失败返回503)replyCode = ftpClient.getReplyCode();// 根据状态码检测ftp的连接,调用isPositiveCompletion(reply)-->如果连接成功返回true,否则返回falseif (!FTPReply.isPositiveCompletion(replyCode)) {logger.info("connect ftp {} failed", ftpProperties.getHost());// 连接失败,断开连接ftpClient.disconnect();return null;}logger.info("replyCode:" + replyCode);} catch (IOException e) {logger.error("connect fail:" + e.toString());return null;}return ftpClient;}private void toPathOrCreateDir(FTPClient ftp, String filePath) throws IOException {String[] dirs = filePath.split("/");for (String dir : dirs) {if (StrUtil.isBlank(dir)) {continue;}if (!ftp.changeWorkingDirectory(dir)) {ftp.makeDirectory(dir);ftp.changeWorkingDirectory(dir);}}}}

五、相关工具类

package com.example.demo.tools;import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;/*** @Author: meng* @Description: 文件转换工具类* @Date: 2023/6/30 14:13* @Version: 1.0*/
public class FileUtils {/*** MultipartFile 转 File* @param file* @throws Exception*/public static File multipartFileToFile(MultipartFile file) throws Exception {File toFile = null;if(file.equals("")||file.getSize()<=0){file = null;}else {InputStream ins = null;ins = file.getInputStream();toFile = new File(file.getOriginalFilename());toFile = inputStreamToFile(ins, toFile);ins.close();}return toFile;}private static File inputStreamToFile(InputStream ins, File file) {try {OutputStream os = new FileOutputStream(file);int bytesRead = 0;byte[] buffer = new byte[8192];while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {os.write(buffer, 0, bytesRead);}os.close();ins.close();return file;} catch (Exception e) {e.printStackTrace();}return null;}}

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

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

相关文章

Web3在HTML中获取 MetaMask 启用的用户列表

当然 我们还是要先启动ganache环境 然后 通过MetaMask 导入一些用户 然后 我们需要在页面中引入 web3.min.js 如果您还没有这个文件 可以查看我的文章web3.js获取导入 然后我访问官网 https://learnblockchain.cn/docs/web3.js/web3-eth.html#getchainid 打开后 先来到 web3.…

apple pencil二代建议买吗?性价比高的触控笔测评

因为ipad的强大功能&#xff0c;不少人已经开始使用ipad了&#xff0c;随之也越来越普及。大屏幕上的学习效果很好&#xff0c;但用来刷剧以及打游戏就没什么意思了。如果你不想买一支价格很贵的苹果电容笔&#xff0c;或是只想用来做笔记&#xff0c;你可以考虑一下平替电容笔…

机器学习——无监督学习

聚类 问题描述 训练数据&#xff1a; D { x 1 , x 2 , ⋯ , x m } D\lbrace x_1,x_2,\cdots,x_m\rbrace D{x1​,x2​,⋯,xm​}&#xff0c;其中每个数据为 n n n 维向量 x i ( x i 1 , x i 2 , ⋯ , x i n ) x_i(x_{i1},x_{i2},\cdots,x_{in}) xi​(xi1​,xi2​,⋯,xin​…

测试背了4年“锅“,测试缺陷总结整理(细致)“锅“终丢掉了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 缺陷分析也是测试…

MySQL——变量与游标

今天我们来一起学习MySQL中的变量&#xff08;系统变量与用户变量&#xff09;&#xff0c;以及什么是游标&#xff0c;游标如何使用&#xff1f; 1. 变量 在 MySQL 数据库的存储过程和函数中&#xff0c;可以使用变量来存储查询或计算的中间结果数据&#xff0c;或者输出最终…

RabbitMQ快速上手(延迟队列)

安装 官网 参考文章&#xff1a; ​ https://blog.csdn.net/miaoye520/article/details/123207661 ​ https://blog.csdn.net/lvoelife/article/details/126658695 安装Erlang&#xff0c;并添加环境变量ERLANG_HOME&#xff0c;命令行运行erl 安装rabbitmq&#xff0c;rab…

时间序列预测的20个基本概念总结

1、时间序列 时间序列是一组按时间顺序排列的数据点 比如&#xff1a; 每小时的气压每年的医院急诊按分钟计算的股票价格 2、时间序列的组成部分 时间序列数据有三个主要组成部分。 趋势季节性残差或白噪声 3、趋势 在时间序列中记录的长期缓慢变化/方向。 4、季节性 …

【软件架构模式】—微内核架构

欢迎回到软件架构模式博客系列。这是本系列的第 4 章&#xff0c;我们将讨论微内核架构模式 概述&#xff1a; 内核模式也被称为插件架构模式。将附加应用程序功能作为插件添加到核心应用程序&#xff0c;以提供可扩展性以及功能分离和隔离。 这种模式由两种类型的架构组件组…

SpringSecurity实现前后端分离登录授权详解

在介绍完SpringSecurity实现前后端分离认证之后&#xff0c;然后就是SpringSecurity授权&#xff0c;在阅读本文章之前可以先了解一下作者的上一篇文章SpringSecurity认证SpringSecurity实现前后端分离登录token认证详解_山河亦问安的博客-CSDN博客。 目录 1. 授权 1.1 权限系…

ERR! code ERR_SOCKET_TIMEOUT

问题 安装npm包&#xff0c;终端报错ERR! code ERR_SOCKET_TIMEOUT ERR! code ERR_SOCKET_TIMEOUT详细问题 笔者运行以下命令重新安装依赖项&#xff1a; npm install控制台报错&#xff0c;具体报错信息如下 npm ERR! code ERR_SOCKET_TIMEOUT npm ERR! network Socket t…

360手机刷机 360手机Xposed框架安装 360手机EdXposed、LSP 360手机xposed模块

360手机刷机 360手机Xposed框架安装 360手机EdXposed、LSP 360手机xposed模块 参考&#xff1a;360手机-360刷机360刷机包twrp、root 360刷机包360手机刷机&#xff1a;360rom.github.io 【前言】 手机须Twrp或root后&#xff0c;才可使用与操作Xposed安装后&#xff0c;重启…

Spring Boot 中的 @Transactional 注解是什么,原理,如何使用

Spring Boot 中的 Transactional 注解是什么&#xff0c;原理&#xff0c;如何使用 简介 在 Spring Boot 中&#xff0c;Transactional 注解是非常重要的一个注解&#xff0c;用于实现数据库事务的管理。通过使用 Transactional 注解&#xff0c;我们可以很方便地对事务进行控…