环境为springboot项目:

Maven依赖:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/>
    </parent>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
<dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.3</version>
        </dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

application.yml配置文件:

#ftp配置
ftp:
  userName: //账号
  password: //密码
  host: //192.168.1.000//地址
  outPost: 192.168.1.000//地址
  port: 21//端口
  pathPrefix: transportation/  //上传文件路径


FtpUtils工具:


package com.knd.common.utils;

import com.knd.enums.FileTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.SocketException;
import java.util.*;

/**
 * @Description: ftp工具类
 */
@Slf4j
@Component
@ConfigurationProperties(prefix = "ftp")//读取配置文件的值
public class FtpUtils {
    public static String userName;
    public static String password;
    public static String host;
    public static String outPost;
    public static int port;
    public static String pathPrefix;

    public void setOutPost(String outPost) {
        FtpUtils.outPost = outPost;
    }

    public void setUserName(String userName) {
        FtpUtils.userName = userName;
    }

    public void setPassword(String password) {
        FtpUtils.password = password;
    }

    public void setHost(String host) {
        FtpUtils.host = host;
    }

    public void setPort(int port) {
        FtpUtils.port = port;
    }

    public void setPathPrefix(String pathPrefix) {
        FtpUtils.pathPrefix = pathPrefix;
    }

    /**
     * 获取FTPClient对象
     *
     * @return
     */
    public static FTPClient getFTPClient() {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient = new FTPClient();
            ftpClient.connect(host, port);// 连接FTP服务器
            ftpClient.login(userName, password);// 登陆FTP服务器
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                log.error("未连接到FTP,,请正确配置。");
                ftpClient.disconnect();
            } else {
                log.info("FTP连接成功。");
            }
        } catch (SocketException e) {
            e.printStackTrace();
            log.error("FTP的IP地址可能错误,请正确配置。错误明细:" + e.getMessage());

        } catch (IOException e) {
            e.printStackTrace();
            log.error("FTP的端口错误,请正确配置。错误明细:" + e.getMessage());

        }
        return ftpClient;
    }

    /**
     * 从FTP服务器下载文件
     *
     * @param ftpPath   FTP服务器中文件所在路径 格式: ftptest/aa
     * @param localPath 下载到本地的位置 格式:H:/download
     * @param fileName  文件名称
     */
    public static void downloadFtpFile(String ftpPath, String localPath, String fileName) {
        FTPClient ftpClient = null;
        try {
            ftpClient = getFTPClient();
            ftpClient.setControlEncoding("UTF-8"); // 中文支持
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            ftpClient.changeWorkingDirectory(ftpPath);
            File localFile = new File(localPath + File.separatorChar + fileName);
            OutputStream os = new FileOutputStream(localFile);
            ftpClient.retrieveFile(fileName, os);
            os.close();
            ftpClient.logout();

        } catch (FileNotFoundException e) {
            System.out.println("没有找到" + ftpPath + "文件");
            e.printStackTrace();
        } catch (SocketException e) {
            System.out.println("连接FTP失败.");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件读取错误。");
            e.printStackTrace();
        }

    }

    /**
     * 下载文件
     *
     * @param ftpPath  文件路径
     * @param fileName 文件名
     * @return
     * @date 2019/3/14 13:13
     */
    public static void downloadFtpFile(String ftpPath, String fileName, OutputStream out) {
        FTPClient ftpClient = null;
        try {
            ftpClient = getFTPClient();
            ftpClient.setControlEncoding("UTF-8"); // 中文支持
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            ftpClient.changeWorkingDirectory(ftpPath);
            ftpClient.retrieveFile(fileName, out);
            ftpClient.logout();
        } catch (FileNotFoundException e) {
            System.out.println("没有找到" + ftpPath + "文件");
            e.printStackTrace();
        } catch (SocketException e) {
            System.out.println("连接FTP失败.");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件读取错误。");
            e.printStackTrace();
        }

    }

    /**
     * 把ftp上的文件copy到指定目录文件下
     *
     * @param getDirPath //源文件路径
     * @param path       //FTP服务器中文件所在路径
     * @param fileName   //文件全名(加上后缀)
     * @date 2020/3/13 9:37
     */
    public static String retrieveFile(String getDirPath, String path, String fileName) {
        OutputStream out = null;
        InputStream inputStream = null;
        try {
            out = new ByteArrayOutputStream();
            downloadFtpFile(getDirPath, out);
            inputStream = new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray());
            return uploadFile(path, fileName, inputStream);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                out.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 文件下载
     *
     * @param ftpPathAndFileName 文件路径+文件名  即上传文件成功后返回的路径
     * @param out
     */
    public static void downloadFtpFile(String ftpPathAndFileName, OutputStream out) {
        Map<String, String> map = extractFtpPathAndFileName(ftpPathAndFileName);
        downloadFtpFile(map.get("ftpPath"), map.get("FileName"), out);
    }

    /**
     * Description: 向FTP服务器上传文件
     *
     * @param ftpPath  FTP服务器中文件所在路径 格式: ftptest/aa
     * @param fileName ftp文件名称
     * @param input    文件流
     * @return 成功返回路径加文件名, 失败返回null
     */
    public static String uploadFile(String ftpPath,
                                    String fileName, InputStream input) {
        if (StringUtils.isBlank(ftpPath) || StringUtils.isBlank(fileName) || input == null) {
            return null;
        }
        if (ftpPath.charAt(ftpPath.length() - 1) != '/') {
            ftpPath += "/";
        }
        ftpPath = pathPrefix + ftpPath;
        FTPClient ftpClient = null;
        try {
            int reply;
            ftpClient = getFTPClient();
            reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                return null;
            }
            BufferedInputStream bufferedInputStream = new BufferedInputStream(input);
            ftpClient.setBufferSize(1024 * 1024);
            ftpClient.setControlEncoding("UTF-8"); // 中文支持
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            createDir(ftpPath);
            ftpClient.changeWorkingDirectory(ftpPath);
            ftpClient.storeFile(fileName, bufferedInputStream);
            input.close();
            ftpClient.logout();
            return ftpPath + fileName;
        } catch (IOException e) {
//            e.printStackTrace();
            log.error("ftp文件上传异常----->" + e.getMessage(), e);
            return null;
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException ioe) {
                    log.error("ftp流关闭异常----->" + ioe.getMessage(), ioe);
                }
            }
        }
    }

    /**
     * 上传文件
     *
     * @param ftpPath
     * @param file
     * @return 返回ftpPath和文件名拼接的路径
     */
    public static String uploadFile(String ftpPath,
                                    MultipartFile file) throws IOException {
        if (StringUtils.isBlank(ftpPath) || file == null || file.isEmpty()) {
            return null;
        }
        String fileName = UUID.randomUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        String b = uploadFile(ftpPath, fileName, file.getInputStream());
        if (b != null) {
            return b;
        }
        return null;
    }

    /**
     * 转换文件路径
     *
     * @param fileTypeEnum 文件类型
     * @return
     */
    public static String getFilePathStr(FileTypeEnum fileTypeEnum) {
        //定义日期
        Date date = new Date();
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        String day = c.get(Calendar.YEAR) + "-" + c.get(Calendar.MONTH) + "-" + c.get(Calendar.DATE);
        String fileType = fileTypeEnum.getMsg();
        String filePathStr = day + "/" + fileType;
        return filePathStr;
    }

    /**
     * 创建目录(有则切换目录,没有则创建目录)
     *
     * @param dir
     * @return
     */
    public static boolean createDir(String dir) {
        FTPClient ftpClient = getFTPClient();
        if (!StringUtils.isNotBlank(dir)) {
            return true;
        }
        String d;
        try {
            //目录编码,解决中文路径问题
            d = new String(dir.toString().getBytes("GBK"), "iso-8859-1");
            //尝试切入目录
            if (ftpClient.changeWorkingDirectory(d)) {
                return true;
            }
            String[] arr = dir.split("/");
            StringBuffer sbfDir = new StringBuffer();
            //循环生成子目录
            for (String s : arr) {
                sbfDir.append("/");
                sbfDir.append(s);
                //目录编码,解决中文路径问题
                d = new String(sbfDir.toString().getBytes("GBK"), "iso-8859-1");
                //尝试切入目录
                if (ftpClient.changeWorkingDirectory(d)) {
                    continue;
                }
                if (!ftpClient.makeDirectory(d)) {
//                    System.out.println("[失败]ftp创建目录:" + sbfDir.toString());
                    log.error("[失败]ftp创建目录:" + sbfDir.toString());
                    return false;
                }
                System.out.println("[成功]创建ftp目录:" + sbfDir.toString());
            }
            //将目录切换至指定路径
            return ftpClient.changeWorkingDirectory(d);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件目录切换/创建异常----->" + e.getMessage(), e);
            return false;
        }
    }


    /**
     * * 删除文件 *
     *
     * @param pathName FTP服务器保存目录
     * @param fileName 要删除的文件名称
     * @return
     */
    public static boolean deleteFile(String pathName, String fileName) {
        FTPClient ftpClient = getFTPClient();
        boolean flag = false;
        try {
            System.out.println("开始删除文件");
            // 切换FTP目录
            ftpClient.changeWorkingDirectory(pathName);
            ftpClient.dele(fileName);
            ftpClient.logout();
            flag = true;
            System.out.println("删除文件成功");
        } catch (Exception e) {
            System.out.println("删除文件失败");
            e.printStackTrace();
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    /**
     * 删除文件
     *
     * @param ftpPathAndFileName 文件路径+文件名  即上传文件成功后返回的路径
     * @return
     */
    public static boolean deleteFile(String ftpPathAndFileName) {
        Map<String, String> map = extractFtpPathAndFileName(ftpPathAndFileName);
        return deleteFile(map.get("ftpPath"), map.get("FileName"));
    }

    /**
     * 通过路径获取ftp路径和文件名
     *
     * @param ftpPathAndFileName
     * @return
     */
    public static Map<String, String> extractFtpPathAndFileName(String ftpPathAndFileName) {
        if (StringUtils.isBlank(ftpPathAndFileName)
                || ftpPathAndFileName.lastIndexOf("/") <= 0
                || ftpPathAndFileName.charAt(ftpPathAndFileName.length() - 1) == '/'
        ) {
            throw new IllegalArgumentException("ftpPathAndFileName参数错误,该参数为文件路径+文件名");
        }
        Map<String, String> ret = new HashMap<>();
        String ftpPath = ftpPathAndFileName.substring(0, ftpPathAndFileName.lastIndexOf("/") + 1);
        String FileName = ftpPathAndFileName.substring(ftpPathAndFileName.lastIndexOf("/") + 1);
        ret.put("ftpPath", ftpPath);
        ret.put("FileName", FileName);
        return ret;
    }
}


FileTypeEnum枚举:


@Getter
@Slf4j
public enum FileTypeEnum {

    IDCARDZ(1, "xx", "xxImg"),
    IDCARDF(2, "yyy", "yyyImg"),


 FileTypeEnum(Integer code, String msg, String path) {
        this.code = code;
        this.path = path;
        this.msg = msg;
    }

    private Integer code;
    private String msg;
    private String path;

    public static String getPath(Integer code) {
        if (code == null) {
            throw new BDException("未知文件类型");
        }
        Optional<FileTypeEnum> optional = stream(FileTypeEnum.values()).filter(x -> Objects.equals(x.code, code)).findFirst();
        if (optional.isPresent()) {
            return optional.get().path;
        }
        throw new BDException("未知文件类型");
    }

    public static FileTypeEnum getEnum(Integer code) {
        if (code == null) {
            return null;
        }
        return stream(FileTypeEnum.values()).filter(x -> Objects.equals(x.code, code)).findFirst().orElse(null);
    }
}