目录
一、引入依赖
二、配置文件
三、Controller层
四、Service层
五、相关工具类
由于服务在内网部署,需要使用ftp服务器管理文件,总结如下
commons-net commons-net3.9.0 cn.hutool hutool-all5.7.22
Tip:使用commons-net 3.9.0版本,之前的版本有漏洞
ftp: basePath: / host: 192.168.1.100 httpPath: ftp://192.168.1.100 password: demo port: 21 username: 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; }
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 { @Autowired private 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); } }
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()); @Autowired private FtpProperties ftpProperties; // 方式一 @Override public 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转化为File File file = null; try { // file = FileUtils.multipartFileToFile(multipartFile); flag = uploadFileToFtp(multipartFile.getInputStream(), newFileName + suffix, today, ftpClient); } 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 Response uploadMultipartFile2(MultipartFile multipartFile, HttpServletRequest request) { FTPClient ftpClient = connectFtpServer(); if (multipartFile == null || multipartFile.isEmpty()) { return R.error(Constants.FILE_EMPTY); } //获取原文件后缀名 String originalFileName = multipartFile.getOriginalFilename(); String suffix = originalFileName.substring(originalFileName.lastIndexOf('.')); //使用UUID生成新文件名 String newFileName = IdUtil.fastSimpleUUID(); //当前日期字符串 String today = separator + DateUtil.date().toString(DATE_FORMAT); Boolean flag = false; try { flag = uploadFileToFtp(multipartFile.getInputStream(), newFileName + suffix, today, ftpClient); } catch (Exception e) { logger.error("文件上传失败:", e); } if (!flag) { logger.info("文件上传失败"); return R.error("文件上传失败"); } 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类型,上传成功返回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 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-8 response.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()); } } } } @Override public 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,否则返回false if (!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; } }