zoukankan      html  css  js  c++  java
  • springboot结合FTP服务器实现文件上传

    关于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 }
    乾坤未定,你我皆是黑马
  • 相关阅读:
    [转]进程的用户栈和内核栈
    什么是URL,URL格式
    设计灵感
    Spring源码学习相关记录
    HTML图片标签路径解析
    一次Spring Bean初始化顺序问题排查记录
    是要面向对象,还是简单粗暴?
    2018/07/26学习节点记录
    数据结构-堆 Java实现
    2018 ICPC 徐州邀请赛 总结
  • 原文地址:https://www.cnblogs.com/liuqijia/p/11457497.html
Copyright © 2011-2022 走看看