关于springboot配合Ftp服务器实现文件上传(单/多文件)的实例
jar包、配置信息、配置类、上传的工具类、生成新文件名的工具类、单文件以及多文件上传的业务层、用到的枚举
需要的jar包:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.aaa.liu.ftp 2019-9-1-ftp 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent 1.5.22.RELEASE org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.0 mysql mysql-connector-java 5.1.38 com.alibaba druid 1.1.10 org.springframework.boot spring-boot-starter-thymeleaf com.fasterxml.jackson.core jackson-databind 2.8.1 org.springframework.boot spring-boot-configuration-processor true joda-time joda-time 2.9.4 com.fasterxml.jackson.core jackson-core 2.8.1 com.fasterxml.jackson.core jackson-databind 2.8.1 com.fasterxml.jackson.core jackson-annotations 2.8.1 com.alibaba fastjson 1.2.17 commons-fileupload commons-fileupload 1.3.1 org.apache.httpcomponents httpclient 4.3.5 commons-net commons-net 3.5 net.sourceforge.nekohtml nekohtml 1.9.21
配置信息:resources/application.properties:
1 server.port=8081 2 server.context-path=/ 3 4 spring.datasource.driver-class-name=com.mysql.jdbc.Driver 5 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 6 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?userSSL=false 7 spring.datasource.username=root 8 spring.datasource.password=123456 9 mybatis.type-aliases-package=com.aaa.liu.ftp.model 10 mybatis.mapper-locations=classpath:mapper/*Mapper.xml 11 12 spring.thymeleaf.cache=false 13 spring.thymeleaf.mode=LEGACYHTML5 14 15 #ftp服务器的地址 16 spring.ftp.host=192.168.134.139 17 #ftp服务器的端口号(连接端口号) 18 spring.ftp.port=21 19 #ftp的用户名 20 spring.ftp.username=ftpuser 21 #ftp的密码 22 spring.ftp.password=123456 23 #ftp上传的根目录 24 spring.ftp.basePath=/home/ftp/www 25 #回显地址 26 spring.ftp.httpPath=http://192.168.134.139
在Java代码中根据配置信息创建配置类:/config/FtpProperties.class
1 package com.aaa.liu.ftp.config; 2 3 import org.springframework.boot.context.properties.ConfigurationProperties; 4 import org.springframework.stereotype.Component; 5 6 /** 7 * @Author 刘其佳 8 * @DateTime 2019/9/1 23:03 9 * @Project_Name 2019-9-1-ftp 10 */ 11 @Component 12 @ConfigurationProperties(prefix = "spring.ftp") 13 public class FtpProperties { 14 private String host; 15 private String port; 16 private String username; 17 private String password; 18 private String basePath; 19 private String httpPath; 20 21 public String getHost() { 22 return host; 23 } 24 25 public void setHost(String host) { 26 this.host = host; 27 } 28 29 public String getPort() { 30 return port; 31 } 32 33 public void setPort(String port) { 34 this.port = port; 35 } 36 37 public String getUsername() { 38 return username; 39 } 40 41 public void setUsername(String username) { 42 this.username = username; 43 } 44 45 public String getPassword() { 46 return password; 47 } 48 49 public void setPassword(String password) { 50 this.password = password; 51 } 52 53 public String getBasePath() { 54 return basePath; 55 } 56 57 public void setBasePath(String basePath) { 58 this.basePath = basePath; 59 } 60 61 public String getHttpPath() { 62 return httpPath; 63 } 64 65 public void setHttpPath(String httpPath) { 66 this.httpPath = httpPath; 67 } 68 }
上传的工具类:
1 package com.aaa.liu.ftp.utils; 2 3 import org.apache.commons.net.ftp.FTP; 4 import org.apache.commons.net.ftp.FTPClient; 5 import org.apache.commons.net.ftp.FTPReply; 6 7 import java.io.IOException; 8 import java.io.InputStream; 9 10 /** 11 * @Author 刘其佳 12 * @DateTime 2019/9/2 8:16 13 * @Project_Name 2019-9-1-ftp 14 */ 15 public class FtpUtil { 16 public static Boolean uploadFile(String host, int port, String username, String password, String basePath, 17 String filePath, String fileName, InputStream inputStream) throws IOException { 18 //1、创建临时路径 19 String tempPath=""; 20 //2、创建FTPClient对象(对于连接ftp服务器,以及上传和上传都必须要用到一个对象) 21 FTPClient ftp=new FTPClient(); 22 try{ 23 //3、定义返回的状态码 24 int reply; 25 //4、连接ftp(当前项目所部署的服务器和ftp服务器之间可以相互通讯,表示连接成功) 26 ftp.connect(host,port); 27 //5、输入账号和密码进行登录 28 ftp.login(username,password); 29 //6、接受状态码(如果成功,返回230,如果失败返回503) 30 reply=ftp.getReplyCode(); 31 //7、根据状态码检测ftp的连接,调用isPositiveCompletion(reply)-->如果连接成功返回true,否则返回false 32 if(!FTPReply.isPositiveCompletion(reply)){ 33 //说明连接失败,需要断开连接 34 ftp.disconnect(); 35 return false; 36 } 37 //8、changWorkingDirectory(linux上的文件夹):检测所传入的目录是否存在,如果存在返回true,否则返回false 38 //basePath+filePath-->home/ftp/www/2019/09/02 39 if(!ftp.changeWorkingDirectory(basePath+filePath)){ 40 //9、截取filePath:2019/09/02-->String[]:2019,09,02 41 String[] dirs=filePath.split("/"); 42 //10、把basePath(/home/ftp/www)-->tempPath 43 tempPath=basePath; 44 for (String dir:dirs){ 45 //11、循环数组(第一次循环-->2019) 46 if(null==dir||"".equals(dir)) 47 continue;//跳出本地循环,进入下一次循环 48 //12、更换临时路径:/home/ftp/www/2019 49 tempPath += "/" + dir; 50 //13、再次检测路径是否存在(/home/ftp/www/2019)-->返回false,说明路径不存在 51 if(!ftp.changeWorkingDirectory(tempPath)){ 52 //14、makeDirectory():创建目录 返回Boolean雷类型,成功返回true 53 if(!ftp.makeDirectory(tempPath)){ 54 return false; 55 }else { 56 //15、严谨判断(重新检测路径是否真的存在(检测是否创建成功)) 57 ftp.changeWorkingDirectory(tempPath); 58 } 59 } 60 } 61 } 62 //16.把文件转换为二进制字符流的形式进行上传 63 ftp.setFileType(FTP.BINARY_FILE_TYPE); 64 //17、这才是真正上传方法storeFile(filename,input),返回Boolean雷类型,上传成功返回true 65 if (!ftp.storeFile(fileName, inputStream)) { 66 return false; 67 } 68 // 18.关闭输入流 69 inputStream.close(); 70 // 19.退出ftp 71 ftp.logout(); 72 } catch (IOException e) { 73 e.printStackTrace(); 74 throw new IOException(e); 75 } finally { 76 if (ftp.isConnected()) { 77 try { 78 // 20.断开ftp的连接 79 ftp.disconnect(); 80 } catch (IOException ioe) { 81 ioe.printStackTrace(); 82 } 83 } 84 } 85 return true; 86 } 87 }
生成新文件名的工具类:
1 package com.aaa.liu.ftp.utils; 2 3 import java.util.Random; 4 5 /** 6 * @Author 刘其佳 7 * @DateTime 2019/9/2 8:16 8 * @Project_Name 2019-9-1-ftp 9 */ 10 11 12 public class FileNameUtil { 13 public static String getFileName(Integer userId){ 14 //1、获取当前时间的毫秒数 15 long currentTimeMillis = System.currentTimeMillis(); 16 //2、创建Random对象,获取0-999之间随机数 17 Random random=new Random(); 18 int randomNum = random.nextInt(999); 19 //3、最终的文件名 20 //%03d: %是占位符 3:3位数 0:不够3位数补零 d:数字 21 String fileName = currentTimeMillis + userId + String.format("%03d", randomNum); 22 //0补在后面的写法 23 // String fileName = currentTimeMillis + userId + String.format("%3d0", randomNum); 24 //4、返回文件名 25 return fileName; 26 } 27 28 public static void main(String[] args) { 29 String fileName = FileNameUtil.getFileName(1); 30 System.out.println(fileName); 31 } 32 33 }
单文件上传的业务层:
1 package com.aaa.liu.ftp.service; 2 3 import com.aaa.liu.ftp.config.FtpProperties; 4 import com.aaa.liu.ftp.mapper.UserMapper; 5 import com.aaa.liu.ftp.model.User; 6 import com.aaa.liu.ftp.statuscode.StatusEnum; 7 import com.aaa.liu.ftp.utils.FileNameUtil; 8 import com.aaa.liu.ftp.utils.FtpUtil; 9 import org.joda.time.DateTime; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Service; 12 import org.springframework.web.multipart.MultipartFile; 13 import javax.servlet.http.HttpSession; 14 import java.io.File; 15 import java.util.HashMap; 16 import java.util.Map; 17 18 /** 19 * @Author 刘其佳 20 * @DateTime 2019/9/2 20:52 21 * @Project_Name 2019-9-1-ftp 22 */ 23 @Service 24 public class DoUploadService0 { 25 @Autowired 26 FtpProperties ftpProperties; 27 @Autowired 28 UserMapper userMapper; 29 30 public MapdoUpload(MultipartFile multipartFile, HttpSession session){ 31 Map resultMap=new HashMap (); 32 try { 33 //老文件名 34 String oldFileName = multipartFile.getOriginalFilename(); 35 // 取出session中存的用户user 36 User user = (User) session.getAttribute("user"); 37 //取出user中的id 38 Integer id = user.getId(); 39 //根据id调用工具类生成新文件名 40 String newFileName = FileNameUtil.getFileName(id); 41 //截取老文件名的后缀 42 String substring = oldFileName.substring(oldFileName.lastIndexOf(".")); 43 //将后缀放在新文件名的后面 44 newFileName = newFileName + substring; 45 //生成路径 46 String filePath = new DateTime().toString("yyyy/MM/dd"); 47 //上传 48 Boolean resultBoolean = FtpUtil.uploadFile(ftpProperties.getHost(), Integer.parseInt(ftpProperties.getPort()), ftpProperties.getUsername(), ftpProperties.getPassword(), ftpProperties.getBasePath(), filePath, newFileName, multipartFile.getInputStream()); 49 //判断是否上传成功 50 if (resultBoolean) { 51 //上传成功 52 String head = ftpProperties.getHttpPath() + File.separator + filePath + File.separator + newFileName; 53 user.setHead(head); 54 //调用修改方法,更新数据库中的头像名 55 int i = userMapper.updateHead(user); 56 //如果更新成功 57 if (i > 0) { 58 resultMap.put(StatusEnum.SUCCESS.getCodeName(), StatusEnum.SUCCESS.getCode()); 59 resultMap.put(StatusEnum.SUCCESS.getResultName(), head); 60 } else { 61 //如果没有更新成功 62 resultMap.put(StatusEnum.FAILED.getCodeName(), StatusEnum.FAILED.getCode()); 63 resultMap.put(StatusEnum.SUCCESS.getResultName(), head); 64 } 65 } else { 66 //如果没有上传成功 67 resultMap.put(StatusEnum.ERROR.getCodeName(), StatusEnum.ERROR.getCode()); 68 } 69 } catch (Exception e) { 70 e.printStackTrace(); 71 } 72 return resultMap; 73 } 74 75 76 }
多文件上传的业务层:
1 package com.aaa.liu.ftp.service; 2 3 import com.aaa.liu.ftp.config.FtpProperties; 4 import com.aaa.liu.ftp.mapper.UserMapper; 5 import com.aaa.liu.ftp.model.User; 6 import com.aaa.liu.ftp.statuscode.StatusEnum; 7 import com.aaa.liu.ftp.utils.FileNameUtil; 8 import com.aaa.liu.ftp.utils.FtpUtil; 9 import org.joda.time.DateTime; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Service; 12 import org.springframework.transaction.annotation.Transactional; 13 import org.springframework.web.multipart.MultipartFile; 14 15 import javax.servlet.http.HttpSession; 16 import java.io.File; 17 import java.io.IOException; 18 import java.util.HashMap; 19 import java.util.Map; 20 21 /** 22 * @Author 刘其佳 23 * @DateTime 2019/9/2 20:52 24 * @Project_Name 2019-9-1-ftp 25 */ 26 @Service 27 public class DoUploadService { 28 @Autowired 29 FtpProperties ftpProperties; 30 @Autowired 31 UserMapper userMapper; 32 33 public MapdoUpload(MultipartFile[] multipartFiles, HttpSession session) { 34 Map resultMap = new HashMap (); 35 String headAll=""; 36 // 取出session中存的用户user 37 User user = (User) session.getAttribute("user"); 38 //取出user中的id 39 Integer id = user.getId(); 40 //根据id调用工具类生成新文件名 41 String newFileName = FileNameUtil.getFileName(id); 42 43 //对文件数组进行遍历 44 for (MultipartFile multipartFile : multipartFiles) { 45 //老文件名 46 String oldFileName = multipartFile.getOriginalFilename(); 47 //截取老文件名的后缀 48 String substring = oldFileName.substring(oldFileName.lastIndexOf(".")); 49 //将后缀放在新文件名的后面 50 newFileName = newFileName + substring; 51 //生成路径 52 String filePath = new DateTime().toString("yyyy/MM/dd"); 53 //上传 54 try { 55 Boolean resultBoolean = FtpUtil.uploadFile(ftpProperties.getHost(), Integer.parseInt(ftpProperties.getPort()), ftpProperties.getUsername(), ftpProperties.getPassword(), ftpProperties.getBasePath(), filePath, newFileName, multipartFile.getInputStream()); 56 //判断是否上传成功 57 if (resultBoolean) { 58 //上传成功 59 String head = ftpProperties.getHttpPath() + File.separator + filePath + File.separator + newFileName; 60 //将当前图片的地址加进headAll,组合成一个新字符串,每个地址中间用\隔开 61 headAll=headAll+";"+head; 62 } else { 63 //如果没有上传成功 64 resultMap.put(StatusEnum.ERROR.getCodeName(), StatusEnum.ERROR.getCode()); 65 resultMap.put(StatusEnum.SUCCESS.getResultName(), headAll); 66 //只要有一个没有上传成功,直接结束上传,不再继续 67 break; 68 } 69 } catch (Exception e) { 70 e.printStackTrace(); 71 } 72 } 73 user.setHead(headAll); 74 //调用修改方法,更新数据库中的头像名 75 int i = userMapper.updateHead(user); 76 //如果更新成功 77 if (i > 0) { 78 resultMap.put(StatusEnum.SUCCESS.getCodeName(), StatusEnum.SUCCESS.getCode()); 79 resultMap.put(StatusEnum.SUCCESS.getResultName(), headAll); 80 } else { 81 //如果没有更新成功 82 resultMap.put(StatusEnum.FAILED.getCodeName(), StatusEnum.FAILED.getCode()); 83 resultMap.put(StatusEnum.SUCCESS.getResultName(), headAll); 84 } 85 return resultMap; 86 } 87 }
其中用到的枚举值:
1 package com.aaa.liu.ftp.statuscode; 2 3 /** 4 * @Author 刘其佳 5 * @DateTime 2019/9/2 19:50 6 * @Project_Name 2019-9-1-ftp 7 */ 8 public enum StatusEnum { 9 SUCCESS(200,"code","操作成功","result"), 10 FAILED(404,"code","操作失败","result"), 11 ERROR(500,"code","操作无效","result"); 12 private Integer code; 13 private String codeName; 14 private String result; 15 private String resultName; 16 17 StatusEnum(Integer code,String codeName,String result,String resultName){ 18 this.code=code; 19 this.codeName=codeName; 20 this.result=result; 21 this.resultName=resultName; 22 } 23 public Integer getCode() { 24 return code; 25 } 26 27 public void setCode(Integer code) { 28 this.code = code; 29 } 30 31 public String getCodeName() { 32 return codeName; 33 } 34 35 public void setCodeName(String codeName) { 36 this.codeName = codeName; 37 } 38 39 public String getResult() { 40 return result; 41 } 42 43 public void setResult(String result) { 44 this.result = result; 45 } 46 47 public String getResultName() { 48 return resultName; 49 } 50 51 public void setResultName(String resultName) { 52 this.resultName = resultName; 53 } 54 }