SpringBoot+ftp 实现文件的上传、下载与删除

2023-10-31

SpringBoot+ftp 实现文件的上传、下载与删除


不做过多解释,可移植,比较简单方便。

一、引包

3.8.0是目前最新的,除非重大更新,基本用最新的没问题。

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.7</version>
</dependency>

二、配置

配置就是下面的,可以放在application.properties中,也可以单独写一个ftp.properties。看个人,根据读取配置方式而定。

#ftp服务器的地址
ftp.host=127.0.0.1
#ftp服务器的端口号(连接端口号)
ftp.port=21
#ftp的用户名
ftp.username=test
#ftp的密码
ftp.password=123456
#ftp上传的根目录
ftp.basePath=/ZPY
#回显地址
ftp.httpPath=ftp://127.0.0.1

三、代码

3.1配置类

因为项目内配置内容太多,所以各个部分都单独拥有自己的配置文件,因此对应一个自己的配置类,这样比较方便。关键是**@Component注解。如果直接在application.properties中的话,可以使用@value**注解来获取也很方便。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "ftp")
@PropertySource(value = "classpath:config/ftp.properties", encoding = "UTF-8")
public class FtpInteranceEntity {
	/**
	 * ftp服务器的地址
	 */
	private String host;
	/**
	 * ftp服务器的端口号(连接端口号)
	 */
	private String port;
	/**
	 * ftp的用户名
	 */
	private String username;
	/**
	 * ftp的密码
	 */
	private String password;
	/**
	 * ftp上传的根目录
	 */
	private String basePath;
	/**
	 * 回显地址
	 */
	private String httpPath;
	
	//set与get方法省略。。。
}

3.2 接口服务

规范操作,写成内部接口而不是静态方法,原因不再细说。注意的是在springboot项目下,上传的文件是MultipartFile,为了不再项目本地再次存放,进而使用输入流发送。controller层转化一下即可,方便很多。当然也有直接存放File文件的方式或者路径存入,此处不再描述。

InputStream inputStream = file.getInputStream();
ftpService.uploadFile(inputStream, "文件名,要以非中文形式", "ftp服务下的文件目录");

接口包含了上传、下载、删除。下载接口与上传的想法一致,不再中间存放文件,因此使用输出流转发。

package net.cnki.api.service;

import java.io.InputStream;
import java.io.OutputStream;

/**
 * 
 * @author ZhiPengyu
 * @ClassName:    [FtpService.java]
 * @Description:  [上传文件ftp存储服务]
 * @CreateDate:   [2020年9月27日 上午9:42:35]
 */
public interface FtpService {
	
	/**
	 * 上传文件到ftp
	 * @param inputStream 输入流
	 * @param fileName 新的文件名,包含拓展名
	 * @param filePath 保存路径
	 * @return
	 */
	public Boolean uploadFile(InputStream inputStream, String fileName, String filePath);

	/**
	 * 下载ftp文件,直接转到输出流
	 * @param ftpFilePath
	 * @param out
	 */
	public void downloadFileTo(String ftpFilePath,OutputStream out);
	
	/**
	 * 删除ftp文件
	 * @param ftpFilePath ftp下文件路径,根目录开始
	 * @return
	 */
	public Boolean deleteFile(String ftpFilePath);
}

@Service重要,别忘了。方法里实现都有注释。

package net.cnki.api.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.lang3.StringUtils;
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 net.cnki.api.bean.FtpInteranceEntity;
import net.cnki.api.service.FtpService;

@Service
public class FtpServiceImpl implements FtpService {
	Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	FtpInteranceEntity ftpInteranceEntity;

	@Override
	public Boolean uploadFile(InputStream inputStream, 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);
				// 上传文件 参数:上传后的文件名,输入流,,返回Boolean类型,上传成功返回true
				iaOk = ftp.storeFile(fileName, inputStream);
				// 关闭输入流
				inputStream.close();
				// 退出ftp
				ftp.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;
	}

	@Override
	public void downloadFileTo(String ftpFilePath, OutputStream out) {
		FTPClient ftp = connectFtpServer();
		try{
			ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
			ftp.enterLocalPassiveMode();
			ftp.retrieveFile(ftpFilePath, out);
			ftp.logout();
		} catch (Exception e) {
			logger.error("FTP文件下载失败!" + e.toString());
		} finally {
			if (ftp.isConnected()) {
				try {
					ftp.disconnect();
				} catch (IOException ioe) {
					logger.error(ioe.toString());
				}
			}
		}
	}

	@Override
	public Boolean deleteFile(String ftpFilePath) {
		FTPClient ftp = connectFtpServer();
		boolean resu = false;
		try{
//			ftp.changeWorkingDirectory("文件路径");
//			ftp.dele("文件名称,如果写全路径就不需要切换目录了。");
//			deleteFile内同样实现了dele,只不多是对返回结果做了处理,相对方便一点
			resu = ftp.deleteFile(ftpFilePath);
			ftp.logout();
			return resu;
		} catch (Exception e) {
			logger.error("FTP文件删除失败!" + e.toString());
		} finally {
			if (ftp.isConnected()) {
				try {
					ftp.disconnect();
				} catch (IOException ioe) {
					logger.error(ioe.toString());
				}
			}
		}
		return resu;
	}
	
	private FTPClient connectFtpServer() {
		// 创建FTPClient对象(对于连接ftp服务器,以及上传和上传都必须要用到一个对象)
		FTPClient ftpClient = new FTPClient();
		// 设置连接超时时间
		ftpClient.setConnectTimeout(1000 * 30);
		// 设置ftp字符集
		ftpClient.setControlEncoding("utf-8");
		// 设置被动模式,文件传输端口设置,否则文件上传不成功,也不报错
		ftpClient.enterLocalPassiveMode();
		try {
			// 定义返回的状态码
			int replyCode;
			// 连接ftp(当前项目所部署的服务器和ftp服务器之间可以相互通讯,表示连接成功)
			ftpClient.connect(ftpInteranceEntity.getHost());
			// 输入账号和密码进行登录
			ftpClient.login(ftpInteranceEntity.getUsername(), ftpInteranceEntity.getPassword());
			// 接受状态码(如果成功,返回230,如果失败返回503)
			replyCode = ftpClient.getReplyCode();
			// 根据状态码检测ftp的连接,调用isPositiveCompletion(reply)-->如果连接成功返回true,否则返回false
			if (!FTPReply.isPositiveCompletion(replyCode)) {
				logger.info("connect ftp {} failed", ftpInteranceEntity.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 (StringUtils.isEmpty(dir)) {
				continue;
			}
			
			if (!ftp.changeWorkingDirectory(dir)) {
				ftp.makeDirectory(dir);
				ftp.changeWorkingDirectory(dir);
			}
		}
	}

}

3.3controller层示例

上传、下载,删除就不弄了,没什么注意的。

	@PostMapping(value = "submitThesis")
	@Transactional
	public void submitThesis(MultipartFile thesisFile,HttpServletRequest request, HttpServletResponse response) throws IOException, ParseException {
		logger.info("接收提交的论文!");
		try {
			if (thesisFile.isEmpty()) {
				ResponseUtil.out(response, 1002, resultGenerator.getFreeResult(ResultCode.PARAM_IS_BLANK,"请上传论文文件!").toString());
				return;
			}
			if (thesisFile.getSize() > 838860800) {
				ResponseUtil.out(response, 4004, resultGenerator.getFreeResult(ResultCode.DOCUMENT_CON_SIZE_UP).toString());
				return;
			}

			String extension1 = FilenameUtils.getExtension(thesisFile.getOriginalFilename()).toLowerCase();
			String uuidName = UUIDUtil.getUUID() + "." + extension1;
			InputStream inputStream = thesisFile.getInputStream();
			String filePath = ftpInteranceEntity.getBasePath()+DateUtils.getTimePath("/yyyy/MM/dd")+"/"+username;
			boolean isSuccess = ftpService.uploadFile(inputStream, uuidName, filePath);
			logger.info("文件上传ftp文件服务器结果:"+isSuccess);
			
		} catch (Exception e) {
			logger.error("提交论文执行异常:",e);
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚
			ResponseUtil.out(response, 3001, resultGenerator.getFreeResult(ResultCode.API_EXCEPTION).toString());
			return;
		}
		ResponseUtil.out(response, 400, resultGenerator.getFailResult("提交论文失败!").toString());
	}

document不重要,是我业务上的对象,下面代码删掉了,看好怎么下载的就可以,ftp上的文件路径知道就没跑了。

	@GetMapping(value = "downloadFile",produces = "application/octet-stream")
	public void downloadFile(HttpServletRequest request, HttpServletResponse response) throws IOException, ParseException {
		logger.info("下载已上传文件!");
		if (ParametersUtil.isBlank(docId)) {
			ResponseUtil.out(response, 1002, resultGenerator.getFreeResult(ResultCode.PARAM_IS_BLANK).toString());
			return;
		}
		String ftpPath = document.getFilePath();//ftp下文件的存放路径
		try (OutputStream out = response.getOutputStream();){
			//配置返回文件名
			String filename = document.getFileName();//前端需要的文件名,中英文你看着来,我随意
			String extension = FilenameUtils.getExtension(filename).toLowerCase();
			String userAgent = request.getHeader("user-agent").toLowerCase();  
	        if (userAgent.contains("msie") || userAgent.contains("like gecko") ) {
	        	filename = URLEncoder.encode(filename, "UTF-8");// win10 ie edge 浏览器 和其他系统的ie 
	        	filename = filename.replace("+", "%20");
	        } else {  
	        	filename = new String(filename.getBytes("utf-8"), "iso-8859-1");  
	        } 
	        response.setContentLengthLong(document.getFileSize());
			response.addHeader("Content-Disposition", "attachment;fileName=" + filename);// 设置文件名
			response.addHeader("fileName",filename);// 设置文件名
			//配置返回相应类型
			String contype = MimeTypes.getContentType(extension);//application/x-download
			response.setContentType((contype == null?"application/octet-stream":contype)+";charset=UTF-8");
			response.setCharacterEncoding("UTF-8");
			ftpService.downloadFileTo(ftpPath,out);
			out.flush();
		} catch (Exception e) {
			logger.error("下载已上传文件异常:",e);
			ResponseUtil.out(response, 3001, resultGenerator.getFreeResult(ResultCode.API_EXCEPTION).toString());
			return;
		}
		logger.info("下载文件:["+document.getId()+"]-["+document.getTitle()+"]执行结束!");
	}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot+ftp 实现文件的上传、下载与删除 的相关文章

随机推荐

  • EOS 数据签名与公匙验证代码用例

    本文编写了一个小例子诠释了EOS是如何对数据签名与校验的 通过本文可以理解了签名的重要性和数据的不可篡改性 系统 ubuntu 版本为EOS1 1 1 注 因为本文的程序是把EOS里面的钱包和fc工具的代码全部提取出来编译的 这个过程相对复
  • Redis简单入门

    一提起数据库 大多数人可能想到的主要是Oracle MySQL以及Microsoft SQL Server这三大巨头 但除了这三巨头外 很多新兴的数据库也慢慢进入开发者的眼帘 比如最近越来越火的搜索引擎式数据库Elasticsearch D
  • 202303读书笔记

    202302读书笔记 长安的荔枝 只要肯努力 办法总比困难多 长安的荔枝 这本书真是酣畅淋漓啊 读起来一气呵成 以讲故事的口吻叙述 上林署九品小官员 李善德 兢兢业业工作多年 终于借贷买了房 让妻子儿女有一个安身之所 一面沉浸在喜悦里 一面
  • 顺序表----数组结构c++

    include
  • js数组sort()方法按指定顺序排序

    数组的sort 方法可以把数组排序 不传参数的默认按字典排序 sort 方法还接受一个回调函数 按回调函数内代码逻辑排序 该函数要比较两个值 然后返回一个用于说明这两个值的相对顺序的数字 比较函数应该具有两个参数 a 和 b 若 a 小于
  • Centos7 rsync 实现文件同步

    rsync remote sync 是unix及类unix平台下的数据镜像备份软件 它不像FTP那样需要全备份 rsync可以根据数据的变化进行差异备份 从而减少数据流量 提高工作效率 序号 类型 ip 1 server 10 200 13
  • Unity LayerMask 的切换功能的实现

    组里需求 需要做一个按钮用来开启和关闭不同层的渲染 这是一个简单的问题 但是实现起来稍微繁琐 实现一个layermask的切换状态 你可能会做 你需要读取当前的某个层的状态 将该层的状态设置为相反的状态 因为layermask设计为二进制保
  • 记录好项目D13

    记录好项目 你好呀 这里是我专门记录一下从某些地方收集起来的项目 对项目修改 进行添砖加瓦 变成自己的闪亮项目 修修补补也可以成为毕设哦 本次的项目是个宠物商城系统 一 系统介绍 未注册用户 非注册用户 即游客身份 进入宠物官网首页 可以浏
  • redis-cli

    文章目录 集群中手动切换节点 xac xed 问题 查看集群节点 集群中手动切换节点 connect host port xac xed 问题 首先出现这个现象是因为序列化器没设置好 直接 get xac xed x00 x05t x00
  • Python os.walk 遍历指定深度的方法

    用os walk可以遍历多层目录 但是有时需要只遍历指定层数目录 比如 要获取某个目录的1级和2级子目录 可以用下面的方法 coding UTF 8 Python 3 6 import os def get sub dirs root pa
  • xss-labs靶场训练(1~4关)

    第一关 前端F12查看代码 这里只能看到参数test输出在h2里面 可以尝试直接构造payload 成功 后台关键源码 这里是以GET方法获取参数name 并直接输出 没有任何过滤或转义 第二关 尝试上一关的方法 Payload没有执行成功
  • 第四十七节 C++ 匿名函数对象 - lambda 表达式

    函数对象 函数的对象 实现operator 常用于算法中 详见上一节函数对象的讲解 lambda表达式属于函数对象 但其是匿名的 分为 1 一元函数的lambda表达式 不使用捕获列表 使用捕获列表 2 一元谓词的lambda表达式 不使用
  • linux shell脚本双引号转义,Linux Shell脚本中单引号(‘)和双引号(“)的区别

    在Linux操作系统上编写Shell脚本时候 我们是在变量的前面使用 符号来获取该变量的值 通常在脚本中使用 param 这种带双引号的格式 但也有出现使用 param 这种带引号的使用的场景 首先大家看一段例子 root linux na
  • JAVA代码保护工具DashO Pro v10.0.0 Beta 2重磅上线!更新DashO Gradle插件!

    DashO是一个Java和Android的混用程序 它提供企业级应用的加固和屏蔽 大大降低了知识产权盗窃 数据盗窃 盗版和篡改的风险 分层混淆 加密 水印 自动失效 反调试 反篡改 反仿真器 反挂钩 反根设备解决方案 为世界各地的应用程序提
  • 单片机毕业设计 stm32智能电子秤系统设计与实现 - 物联网 嵌入式

    文章目录 0 前言 1 简介 2 主要器件 3 实现效果 4 设计原理 4 1 STM32F103C8T6 4 2 HX711压力传感器 5 部分核心代码 6 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目
  • Simulink Simscape基础仿真电路

    在网上找了挺多关于MATLAB Simulink simscape仿真电路的资料都没有自己想要的 大都是Sympowersystem的教程 最后还是上了YouTube观看了一些教程 现在做下学习记录 由于我电脑上安装了2016和2010两个
  • 身份和访问管理解决方案:混合型IAM

    对于依赖于本地 IT 基础结构和传统安全模型的组织 可以更轻松地验证和授权企业网络内的所有内容 包括设备 用户 应用程序和服务器 尝试从公司网络外部获取访问权限的用户使用虚拟专用网络 VPN 和网络访问控制 NAC 进行身份验证 随着云和远
  • java中equals的重写_Java重写equals方法(重点讲解)

    为什么equals 方法要重写 判断两个对象在逻辑上是否相等 如根据类的成员变量来判断两个类的实例是否相等 而继承Object中的equals方法只能判断两个引用变量是否是同一个对象 这样我们往往需要重写equals 方法 我们向一个没有重
  • Hadoop-The variance for this alert is **MB which is 20% of the **MB average (**MB is the limit)

    The variance for this alert is MB which is 20 of the MB average MB is the limit 1 调整如下阀值 2 检查HDFS文件系统使用率 清空HDFS上的 trash垃
  • SpringBoot+ftp 实现文件的上传、下载与删除

    SpringBoot ftp 实现文件的上传 下载与删除 一 引包 二 配置 三 代码 3 1配置类 3 2 接口服务 3 3controller层示例 不做过多解释 可移植 比较简单方便 一 引包 3 8 0是目前最新的 除非重大更新 基