目录
一、引入依赖
二、配置文件
三、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;}}