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()+"]执行结束!");
	}
  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值