關於springboot配合Ftp服務器實現文件上傳(單/多文件)的實例
jar包、配置信息、配置類、上傳的工具類、生成新文件名的工具類、單文件以及多文件上傳的業務層、用到的枚舉
需要的jar包:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" 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"> <modelVersion>4.0.0</modelVersion> <groupId>com.aaa.liu.ftp</groupId> <artifactId>2019-9-1-ftp</artifactId> <version>1.0-SNAPSHOT</version> <!-- fileUpload的包 ftp:commons的包 http joda-time(做時間日期轉換的包) --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.22.RELEASE</version> </parent> <dependencies> <!-- springboot-starter-web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- springboot-mybatis整合包 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <!-- mysql的驅動包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- druid連接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!-- html的thymeleaf模板 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- fastjson包 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.1</version> </dependency> <!-- 添加springboot的進程jar包 里面包含了properties文件的讀取(其實就是包含了@ConfigurationProperties()注解) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- joda time begin --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.9.4</version> </dependency> <!-- joda time end --> <!--json start --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.17</version> </dependency> <!--json end --> <!-- file upload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!-- file upload --> <!-- httpclient begin --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.5</version> </dependency> <!-- httpclient end --> <!-- ftp begin --> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.5</version> </dependency> <!-- ftp end --> <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.21</version> </dependency> </dependencies> </project>
配置信息: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 Map<String,Object> doUpload(MultipartFile multipartFile, HttpSession session){ 31 Map<String,Object> resultMap=new HashMap<String,Object>(); 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 Map<String, Object> doUpload(MultipartFile[] multipartFiles, HttpSession session) { 34 Map<String, Object> resultMap = new HashMap<String, Object>(); 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 }