Java连接FTP服务器并使用ftp连接池进行文件操作指南

 更新时间:2024年08月19日 09:55:50   作者:顾十方  
使用FTP最主要的功能是对文件进行管理,下面这篇文章主要给大家介绍了关于Java连接FTP服务器并使用ftp连接池进行文件操作的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

使用Java连接FTP服务器进行文件相关操作,并且使用FTP连接池降低资源消耗,提高响应速率。

1、导入Pom依赖

         <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.9.0</version>
        </dependency>

2、创建FTP的配置

ftp:
    # 服务器地址
    host: xx.xxx.xx.xxx
    # 端口号
    port: 21
    # 用户名
    userName: xxx
    # 密码
    password: xxxxxxx
    # 工作目录
    workingDirectory: /ftpTest
    # 编码
    encoding: utf-8
    #被动模式
    passiveMode: true
    #连接超时时间
    clientTimeout: 30000
    # 线程数
    threaNum: 1
    # 0=ASCII_FILE_TYPE(ASCII格式),1=EBCDIC_FILE_TYPE,2=LOCAL_FILE_TYPE(二进制文件)
    transferFileType: 2
    # 是否重命名
    renameUploaded: true
    # 重新连接时间
    retryTimes: 1200
    # 缓存大小
    bufferSize: 8192

    # 最大数
    maxTotal: 50
    # 最小空闲
    minldle: 10
    # 最大空闲
    maxldle: 50
    # 最大等待时间
    maxWait: 30000
    # 池对象耗尽之后是否阻塞,maxWait < 0 时一直等待
    blockWhenExhausted: true
    # 取对象时验证
    testOnBorrow: true
    # 回收验证
    testOnReturn: true
    # 创建时验证
    testOnCreate: true
    # 空闲验证
    testWhileldle: false
    # 后进先出
    lifo: false

3、创建FTP配置类

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.apache.commons.net.ftp.FTPClient;

/**
 * Ftp配置类
 */
@Configuration
@ConfigurationProperties(prefix = "ftp")
@Getter
@Setter
public class FtpConfig extends GenericObjectPoolConfig<FTPClient> {

    /**
     * FTP服务器地址
     */
    private String host;

    /**
     * FTP服务器端口
     */
    private Integer port;

    /**
     * FTP用户名
     */
    private String userName;

    /**
     * FTP密码
     */
    private String password;

    /**
     * FTP服务器根目录
     */
    private String workingDirectory;

    /**
     * 传输编码
     */
    String encoding;

    /**
     * 被动模式:在这种模式下,数据连接是由客户程序发起的
     */
    boolean passiveMode;

    /**
     * 连接超时时间
     */
    int clientTimeout;

    /**
     * 线程数
     */
    int threaNum;
    /**
     * 0=ASCII_FILE_TYPE(ASCII格式),1=EBCDIC_FILE_TYPE,2=LOCAL_FILE_TYPE(二进制文件)
     */
    int transferFileType;

    /**
     * 是否重命名
     */
    boolean renameUploaded;

    /**
     * 重新连接时间
     */
    int retryTimes;

    /**
     * 缓存大小
     */
    int bufferSize;

    /**
     * 最大数
     */
    int maxTotal;

    /**
     * 最小空闲
     */
    int minldle;

    /**
     * 最大空闲
     */
    int maxldle;

    /**
     * 最大等待时间
     */
    int maxWait;
    /**
     *  池对象耗尽之后是否阻塞,maxWait < 0 时一直等待
     */
    boolean blockWhenExhausted;
    /**
     * 取对象时验证
     */
    boolean testOnBorrow;
    /**
     * 回收验证
     */
    boolean testOnReturn;
    /**
     * 创建时验证
     */
    boolean testOnCreate;
    /**
     * 空闲验证
     */
    boolean testWhileldle;
    /**
     * 后进先出
     */
    boolean lifo;
}

4、创建工厂连接对象并注入配置

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * FtpClient 工厂连接对象
 */
@Component
@Slf4j
public class FTPClientFactory implements PooledObjectFactory<FTPClient> {
    /**
     * 注入 ftp 连接配置
     */
    @Autowired
    FtpConfig config;

    /**
     * 创建连接到池中
     *
     * @return
     * @throws Exception
     */
    @Override
    public PooledObject<FTPClient> makeObject() throws Exception {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setConnectTimeout(config.getClientTimeout());
        ftpClient.connect(config.getHost(), config.getPort());
        int reply = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            ftpClient.disconnect();
            return null;
        }
        boolean success;
        if (StringUtils.isBlank(config.getUserName())) {
            success = ftpClient.login("anonymous", "anonymous");
        } else {
            success = ftpClient.login(config.getUserName(), config.getPassword());
        }
        if (!success) {
            return null;
        }
        ftpClient.setFileType(config.getTransferFileType());
        ftpClient.setBufferSize(1024);
        ftpClient.setControlEncoding(config.getEncoding());
        if (config.isPassiveMode()) {
            ftpClient.enterLocalPassiveMode();
        }
        log.debug("创建ftp连接");
        return new DefaultPooledObject<>(ftpClient);
    }

    /**
     * 链接状态检查
     *
     * @param pool
     * @return
     */
    @Override
    public boolean validateObject(PooledObject<FTPClient> pool) {
        FTPClient ftpClient = pool.getObject();
        try {
            return ftpClient != null && ftpClient.sendNoOp();
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 销毁连接,当连接池空闲数量达到上限时,调用此方法销毁连接
     *
     * @param pool
     * @throws Exception
     */
    @Override
    public void destroyObject(PooledObject<FTPClient> pool) throws Exception {
        FTPClient ftpClient = pool.getObject();
        if (ftpClient != null) {
            try {
                ftpClient.disconnect();
                log.debug("销毁ftp连接");
            } catch (Exception e) {
                log.error("销毁ftpClient异常,error:", e.getMessage());
            }
        }
    }

    /**
     * 钝化连接,是连接变为可用状态
     *
     * @param p
     * @throws Exception
     */
    @Override
    public void passivateObject(PooledObject<FTPClient> p) throws Exception{
        FTPClient ftpClient = p.getObject();
        try {
            ftpClient.changeWorkingDirectory(config.getWorkingDirectory());
            ftpClient.logout();
            if (ftpClient.isConnected()) {
                ftpClient.disconnect();
            }
        } catch (Exception e) {
            throw new RuntimeException("Could not disconnect from server.", e);
        }
    }

    /**
     * 初始化连接
     *
     * @param pool
     * @throws Exception
     */
    @Override
    public void activateObject(PooledObject<FTPClient> pool) throws Exception {
        FTPClient ftpClient = pool.getObject();
        ftpClient.connect(config.getHost(),config.getPort());
        ftpClient.login(config.getUserName(), config.getPassword());
        ftpClient.setControlEncoding(config.getEncoding());
        ftpClient.changeWorkingDirectory(config.getWorkingDirectory());
        //设置上传文件类型为二进制,否则将无法打开文件
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    }

    /**
     * 获取 FTP 连接配置
     * @return
     */
    public FtpConfig getConfig(){
        return config;
    }
}

5、创建客户端对象service接口

import com.aicut.monitor.config.FtpConfig;
import org.apache.commons.net.ftp.FTPClient;

/**
 * 获取 ftp 客户端对象的接口
 */
public interface FTPPoolService {
    /**
     * 获取ftpClient
     * @return
     */
    FTPClient borrowObject();

    /**
     * 归还ftpClient
     * @param ftpClient
     * @return
     */
    void returnObject(FTPClient ftpClient);

    /**
     * 获取 ftp 配置信息
     * @return
     */
    FtpConfig getFtpPoolConfig();
}

6、创建FTP接口实现类 

import com.aicut.monitor.config.FTPClientFactory;
import com.aicut.monitor.config.FtpConfig;
import com.aicut.monitor.service.FTPPoolService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
@Slf4j
public class FTPPoolServiceImpl implements FTPPoolService {

    /**
     * ftp 连接池生成
     */
    private GenericObjectPool<FTPClient> pool;

    /**
     * ftp 客户端配置文件
     */
    @Autowired
    private FtpConfig config;

    /**
     * ftp 客户端工厂
     */
    @Autowired
    private FTPClientFactory factory;

    /**
     * 初始化pool
     */
    @PostConstruct
    private void initPool() {
        this.pool = new GenericObjectPool<FTPClient>(this.factory, this.config);
    }

    /**
     * 获取ftpClient
     */
    @Override
    public FTPClient borrowObject() {
        if (this.pool != null) {
            try {
                return this.pool.borrowObject();
            } catch (Exception e) {
                log.error("获取 FTPClient 失败 ", e);
            }
        }
        return null;
    }

    /**
     * 归还 ftpClient
     */
    @Override
    public void returnObject(FTPClient ftpClient) {
        if (this.pool != null && ftpClient != null) {
            this.pool.returnObject(ftpClient);
        }
    }

    @Override
    public FtpConfig getFtpPoolConfig() {
        return config;
    }
}

7、FTP工具类

import cn.hutool.core.util.CharsetUtil;
import com.aicut.monitor.enums.DownloadStatus;
import com.aicut.monitor.enums.UploadStatus;
import com.aicut.monitor.enums.uploadImageType;
import com.aicut.monitor.service.FTPPoolService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * FTP工具类
 *
 * @author YJ2023085043
 */
@Component
@Slf4j
public class FtpUtil {

    /**
     * ftp 连接池
     */
    @Autowired
    FTPPoolService ftpPoolService;

    public static final String DIR_SPLIT = "/";

    public static final String HTTP_protocol = "http://";


    /**
     * 上传单个文件
     *
     * @param uploadPath 上传路径
     * @param fileName   文件名
     * @param input      文件输入流
     * @return 上传结果
     */
    public UploadStatus upload(String uploadPath, String fileName, InputStream input) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            // 切换到工作目录
            if (!ftpClient.changeWorkingDirectory(uploadPath)) {
                ftpClient.makeDirectory(uploadPath);
                ftpClient.changeWorkingDirectory(uploadPath);
            }
            // 文件写入
            boolean storeFile = ftpClient.storeFile(fileName, input);
            if (storeFile) {
                log.info("文件:{}上传成功", fileName);
                return UploadStatus.UploadNewFileSuccess;
            } else {
                throw new RuntimeException("ftp文件写入异常");
            }
        } catch (IOException e) {
            log.error("文件:{}上传失败", fileName, e);
            return UploadStatus.UploadNewFileFailed;
        } finally {
                IOUtils.closeQuietly(input);
                ftpPoolService.returnObject(ftpClient);
            }
    }

    /**
     * 从FTP服务器上下载文件,支持断点续传,下载百分比汇报
     *
     * @param ftpPath 远程文件路径
     * @param fileName 远程文件名
     * @param local  本地文件完整绝对路径
     * @return 下载的状态
     * @throws IOException
     */
    public DownloadStatus downloadFile(String ftpPath, String fileName, String local) throws IOException {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        // 设置被动模式,由于Linux安全性考虑,端口没有全部放开,所有被动模式不能用
        ftpClient.enterLocalPassiveMode();
        // 设置以二进制方式传输
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        DownloadStatus result;
        try {
            // 检查远程文件是否存在
            FTPFile[] files = ftpClient.listFiles(ftpPath,file -> file.getName().equals(fileName));
            if (files.length != 1) {
                log.info("远程文件不存在");
                return DownloadStatus.RemoteFileNotExist;
            }
            long lRemoteSize = files[0].getSize();
            File f = new File(local+DIR_SPLIT+fileName);
            // 本地存在文件,进行断点下载
            if (f.exists()) {
                long localSize = f.length();
                // 判断本地文件大小是否大于远程文件大小
                if (localSize >= lRemoteSize) {
                    log.info("本地文件大于远程文件,下载中止");
                    return DownloadStatus.LocalFileBiggerThanRemoteFile;
                }
                // 进行断点续传,并记录状态
                FileOutputStream out = new FileOutputStream(f, true);
                ftpClient.setRestartOffset(localSize);
                InputStream in = ftpClient.retrieveFileStream(ftpPath + DIR_SPLIT + fileName);
                byte[] bytes = new byte[1024];
                long step = lRemoteSize / 100;
                // 文件过小,step可能为0
                step = step == 0 ? 1 : step;
                long process = localSize / step;
                int c;
                while ((c = in.read(bytes)) != -1) {
                    out.write(bytes, 0, c);
                    localSize += c;
                    long nowProcess = localSize / step;
                    if (nowProcess > process) {
                        process = nowProcess;
                        if (process % 10 == 0) {
                            log.info("下载进度:" + process);
                        }
                    }
                }
                in.close();
                out.close();
                boolean isDo = ftpClient.completePendingCommand();
                if (isDo) {
                    result = DownloadStatus.DownloadFromBreakSuccess;
                } else {
                    result = DownloadStatus.DownloadFromBreakFailed;
                }
            } else {
                OutputStream out = new FileOutputStream(f);
                InputStream in = ftpClient.retrieveFileStream(ftpPath + DIR_SPLIT + fileName);
                byte[] bytes = new byte[1024];
                long step = lRemoteSize / 100;
                // 文件过小,step可能为0
                step = step == 0 ? 1 : step;
                long process = 0;
                long localSize = 0L;
                int c;
                while ((c = in.read(bytes)) != -1) {
                    out.write(bytes, 0, c);
                    localSize += c;
                    long nowProcess = localSize / step;
                    if (nowProcess > process) {
                        process = nowProcess;
                        if (process % 10 == 0) {
                            log.info("下载进度:" + process);
                        }
                    }
                }
                in.close();
                out.close();
                boolean upNewStatus = ftpClient.completePendingCommand();
                if (upNewStatus) {
                    result = DownloadStatus.DownloadNewSuccess;
                } else {
                    result = DownloadStatus.DownloadNewFailed;
                }
            }
        } catch (Exception e) {
            log.error("download error", e);
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return DownloadStatus.DownloadNewFailed;
    }

    /**
     * 下载文件到本地 *
     *
     * @param ftpPath     FTP服务器文件目录 *
     * @param ftpFileName 文件名称 *
     * @param localPath   下载后的文件路径 *
     * @return
     */
    public boolean download(String ftpPath, String ftpFileName, String localPath) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        OutputStream outputStream = null;
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.isFile() && file.getName().equals(ftpFileName));
            if (ftpFiles != null && ftpFiles.length > 0) {
                FTPFile ftpFile = ftpFiles[0];
                File localFile = new File(localPath + DIR_SPLIT + ftpFile.getName());
                // 判断本地路径目录是否存在,不存在则创建
                if (!localFile.getParentFile().exists()) {
                    localFile.getParentFile().mkdirs();
                }
                outputStream = Files.newOutputStream(localFile.toPath());
                ftpClient.retrieveFile(ftpFile.getName(), outputStream);

                log.info("fileName:{},size:{}", ftpFile.getName(), ftpFile.getSize());
                log.info("下载文件成功...");
                return true;
            } else {
                log.info("文件不存在,filePathname:{},", ftpPath + DIR_SPLIT + ftpFileName);
            }
        } catch (Exception e) {
            log.error("下载文件失败...",e);
        } finally {
            IOUtils.closeQuietly(outputStream);
            ftpPoolService.returnObject(ftpClient);
        }
        return false;
    }

    /**
     * 下载文件到浏览器 *
     *
     * @param ftpPath     FTP服务器文件目录 *
     * @param ftpFileName 文件名称 *
     * @param response
     * @return
     */
    public void download(HttpServletResponse response, String ftpPath, String ftpFileName)  {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        OutputStream outputStream = null;
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.isFile() && file.getName().equals(ftpFileName));
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf8");
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(ftpFileName,"UTF-8") );
            outputStream = response.getOutputStream();
            if (ftpFiles != null && ftpFiles.length > 0) {
                FTPFile ftpFile = ftpFiles[0];
                ftpClient.retrieveFile(ftpPath+DIR_SPLIT+ftpFile.getName(), outputStream);

                log.info("fileName:{},size:{}", ftpFile.getName(), ftpFile.getSize());
                log.info("下载文件成功...");
            } else {
                log.info("文件不存在,filePathname:{},", ftpPath + DIR_SPLIT + ftpFileName);
            }
        } catch (Exception e) {
            log.error("下载文件失败...",e);
        } finally {
            IOUtils.closeQuietly(outputStream);
            ftpPoolService.returnObject(ftpClient);
        }
    }

    public void ftpZipFileDownload (HttpServletResponse response,String ftpPath) {
        //从FTP上下载文件并打成ZIP包给用户下载
        ZipOutputStream zipOut = null;
        try {
            //文件名称
            String zipFileName = "导出数据.zip";
            response.reset();
            // 设置导出文件头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(zipFileName,"UTF-8") );
            // 定义Zip输出流
            zipOut = new ZipOutputStream(response.getOutputStream());
            zipFTPFile(ftpPath,zipOut,"");
        } catch (IOException e) {
            log.error("当前:"+ftpPath+"下载FTP文件--->下载文件失败:"+e.getMessage());
        } finally {
            // 关闭zip文件输出流
            if (null != zipOut) {
                try {
                    zipOut.closeEntry();
                    zipOut.close();
                } catch (IOException e) {
                    log.error("当前:"+ftpPath+"下载FTP文件--->关闭zip文件输出流出错:"+e.getMessage());
                }
            }
        }
    }

    public void zipFTPFile(String ftpPath, ZipOutputStream zipOut,String foldPath){
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            // 切换到指定目录中,如果切换失败说明目录不存在
            if(!ftpClient.changeWorkingDirectory(ftpPath)){
                log.error("切换目录失败");
                throw new RuntimeException("切换目录失败");
            }
            // 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据
            ftpClient.enterLocalPassiveMode();
            // 遍历路径下的所有文件
            FTPFile[] fileList = ftpClient.listFiles();
            byte[] byteReader = new byte[1024];
            ByteArrayOutputStream os = null;
            for (FTPFile tempFile : fileList) {
                if (tempFile.isFile()) {
                    os = new ByteArrayOutputStream();
                    // 从FTP上下载downFileName该文件把该文件转化为字节数组的输出流
                    ftpClient.retrieveFile(tempFile.getName(), os);
                    byte[] bytes = os.toByteArray();
                    InputStream ins = new ByteArrayInputStream(bytes);

                    int len;
                    zipOut.putNextEntry(new ZipEntry(foldPath + tempFile.getName()));
                    // 读入需要下载的文件的内容,打包到zip文件
                    while ((len = ins.read(byteReader)) > 0) {
                        zipOut.write(byteReader, 0, len);
                    }
                }else{
                    //如果是文件夹,则递归调用该方法
                    zipOut.putNextEntry(new ZipEntry(tempFile.getName() + DIR_SPLIT));
                    zipFTPFile(ftpPath + DIR_SPLIT + tempFile.getName(), zipOut, tempFile.getName() + DIR_SPLIT);
                }
            }
            zipOut.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
    }

    /**
     * 得到某个目录下的文件名列表
     *
     * @param ftpDirPath FTP上的目标文件路径
     * @return
     * @throws IOException
     */
    public List<String> getFileList(String ftpDirPath) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath);
            if (ftpFiles != null && ftpFiles.length > 0) {
                return Arrays.stream(ftpFiles).map(FTPFile::getName).collect(Collectors.toList());
            }
            log.error(String.format("路径有误,或目录【%s】为空", ftpDirPath));
        } catch (Exception e) {
            log.error("获取目录下文件列表失败", e);
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return null;
    }

    /**
     * 删除文件
     *
     * @param ftpPath 服务器文件存储路径
     * @param fileName 文件名
     * @return
     * @throws IOException
     */
    public boolean deleteFile(String ftpPath, String fileName) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            // 在 ftp 目录下获取文件名与 fileName 匹配的文件信息
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.getName().equals(fileName));
            // 删除文件
            if (ftpFiles != null && ftpFiles.length > 0) {
                boolean del;
                String deleteFilePath = ftpPath + DIR_SPLIT + fileName;
                FTPFile ftpFile = ftpFiles[0];
                if (ftpFile.isDirectory()) {
                    //递归删除该目录下的所有文件后删除目录
                    FTPFile[] files = ftpClient.listFiles(ftpPath + DIR_SPLIT + fileName);
                    for (FTPFile file : files) {
                        if(file.isDirectory()){
                            deleteFile(ftpPath + DIR_SPLIT + fileName,file.getName());
                        }else{
                            del = ftpClient.deleteFile(deleteFilePath + DIR_SPLIT + file.getName());
                            log.info(del ? "文件:{}删除成功" : "文件:{}删除失败", file.getName());
                        }
                    }
                    del = ftpClient.removeDirectory(deleteFilePath);
                } else {
                    del = ftpClient.deleteFile(deleteFilePath);
                }
                log.info(del ? "文件:{}删除成功" : "文件:{}删除失败", fileName);
                return del;
            } else {
                log.warn("文件:{}未找到", fileName);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return false;
    }

    /**
     * 上传文件到FTP服务器,支持断点续传
     * @param uploadPath 远程文件存放路径
     * @param fileName 上传文件名
     * @param input 文件输入流
     * @return 上传结果
     * @throws IOException
     */
    public UploadStatus uploadFile(String uploadPath, String fileName, InputStream input) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        UploadStatus result = UploadStatus.UploadNewFileFailed;
        try {
            // 设置PassiveMode传输
            ftpClient.enterLocalPassiveMode();
            // 设置以二进制流的方式传输
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.setControlEncoding(CharsetUtil.UTF_8);
            //切换到工作目录
            if(!ftpClient.changeWorkingDirectory(uploadPath)){
                ftpClient.makeDirectory(uploadPath);
                ftpClient.changeWorkingDirectory(uploadPath);
            }
            // 检查远程是否存在文件
            FTPFile[] files = ftpClient.listFiles(uploadPath,file -> file.getName().equals(fileName));
            if (files.length == 1) {
                long remoteSize = files[0].getSize();
                //根据文件输入流获取文件对象
                File f = getFileFromInputStream(input);
                long localSize = f.length();
                // 文件存在
                if (remoteSize == localSize) {
                    return UploadStatus.FileExits;
                } else if (remoteSize > localSize) {
                    return UploadStatus.RemoteFileBiggerThanLocalFile;
                }
                // 尝试移动文件内读取指针,实现断点续传
                result = uploadFile(fileName, f, ftpClient, remoteSize);
                // 如果断点续传没有成功,则删除服务器上文件,重新上传
                if (result == UploadStatus.UploadFromBreakFailed) {
                    if (!ftpClient.deleteFile(fileName)) {
                        return UploadStatus.DeleteRemoteFaild;
                    }
                    result = uploadFile(fileName, f, ftpClient, 0);
                }
            } else {
                result = uploadFile(fileName, getFileFromInputStream(input), ftpClient, 0);
            }
        } catch (Exception e) {
            log.error("上传文件失败", e);
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return result;
    }

    /**
     * 从输入流中获取文件对象
     * @param inputStream
     * @return
     */
    public static File getFileFromInputStream(InputStream inputStream) {
        File file = null;
        try {
            // 创建临时文件
            file = File.createTempFile("temp", null);

            // 将输入流写入临时文件
            byte[] buffer = new byte[1024];
            int bytesRead;
            try (FileOutputStream outputStream = new FileOutputStream(file)) {
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 递归创建远程服务器目录
     *
     * @param remote    远程服务器文件绝对路径
     * @param ftpClient FTPClient对象
     * @return 目录创建是否成功
     * @throws IOException
     */

    public UploadStatus createDirectory(String remote, FTPClient ftpClient) throws IOException {
        UploadStatus status = UploadStatus.CreateDirectorySuccess;
        String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
        if (!directory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(new String(directory.getBytes(CharsetUtil.UTF_8), CharsetUtil.ISO_8859_1))) {
            // 如果远程目录不存在,则递归创建远程服务器目录
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            } else {
                start = 0;
            }
            end = directory.indexOf("/", start);
            while (true) {
                String subDirectory = new String(remote.substring(start, end).getBytes(CharsetUtil.UTF_8), CharsetUtil.ISO_8859_1);
                if (!ftpClient.changeWorkingDirectory(subDirectory)) {
                    if (ftpClient.makeDirectory(subDirectory)) {
                        ftpClient.changeWorkingDirectory(subDirectory);
                    } else {
                        log.info("创建目录失败");
                        return UploadStatus.CreateDirectoryFail;
                    }
                }
                start = end + 1;
                end = directory.indexOf("/", start);
                // 检查所有目录是否创建完毕
                if (end <= start) {
                    break;
                }
            }
        }
        return status;
    }

    /**
     * 上传文件到服务器,新上传和断点续传
     *
     * @param remoteFileName 远程文件名,在上传之前已经将服务器工作目录做了改变,一定要注意这里的 remoteFile 已经别被编码 ISO-8859-1
     * @param localFile  本地文件File句柄,绝对路径
     * @param ftpClient  FTPClient引用
     * @return
     * @throws IOException
     */
    public UploadStatus uploadFile(String remoteFileName, File localFile, FTPClient ftpClient, long remoteSize) {
        if (null == ftpClient) {
            ftpClient = ftpPoolService.borrowObject();
        }
        if (null == ftpClient) {
            return null;
        }
        UploadStatus status = UploadStatus.UploadNewFileFailed;

        try (RandomAccessFile raf = new RandomAccessFile(localFile, "r");
             OutputStream out = ftpClient.appendFileStream(remoteFileName);) {
            // 显示进度的上传
            log.info("localFile.length():" + localFile.length());
            long step = localFile.length() / 100;
            // 文件过小,step可能为0
            step = step == 0 ? 1 : step;
            long process = 0;
            long localreadbytes = 0L;

            // 断点续传
            if (remoteSize > 0) {
                ftpClient.setRestartOffset(remoteSize);
                process = remoteSize / step;
                raf.seek(remoteSize);
                localreadbytes = remoteSize;
            }
            byte[] bytes = new byte[1024];
            int c;
            while ((c = raf.read(bytes)) != -1) {
                out.write(bytes, 0, c);
                localreadbytes += c;
                if (localreadbytes / step != process) {
                    process = localreadbytes / step;
                    if (process % 10 == 0) {
                        log.info("上传进度:" + process);
                    }
                }
            }
            out.flush();
            raf.close();
            out.close();
            // FTPUtil的upload方法在执行ftpClient.completePendingCommand()之前应该先关闭OutputStream,否则主线程会在这里卡死执行不下去。
            // 原因是completePendingCommand()会一直在等FTP Server返回226 Transfer complete,但是FTP Server只有在接受到OutputStream执行close方法时,才会返回。
            boolean result = ftpClient.completePendingCommand();
            if (remoteSize > 0) {
                status = result ? UploadStatus.UploadFromBreakSuccess : UploadStatus.UploadFromBreakFailed;
            } else {
                status = result ? UploadStatus.UploadNewFileSuccess : UploadStatus.UploadNewFileFailed;
            }
        } catch (Exception e) {
            log.error("uploadFile error ", e);
        }
        return status;
    }

    /**
     * 获取FTP某一特定目录下的文件数量
     *
     * @param ftpDirPath FTP上的目标文件路径
     */
    public Integer getFileNum(String ftpDirPath) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath);
            if (ftpFiles != null && ftpFiles.length > 0) {
                return Arrays.stream(ftpFiles).map(FTPFile::getName).collect(Collectors.toList()).size();
            }
            log.error(String.format("路径有误,或目录【%s】为空", ftpDirPath));
        } catch (IOException e) {
            log.error("文件获取异常:", e);
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return null;
    }

    /**
     * 获取文件夹下文件数量
     * @param ftpPath
     * @return
     */
    public Map<String,String> getDirFileNum(String ftpPath) {
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            Integer sum  = 0;
            Map<String,String> map = new HashMap<>();
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath);
            if (ftpFiles != null && ftpFiles.length > 0) {
                for (FTPFile file : ftpFiles) {
                    if (file.isDirectory()) {
                        sum += getFileNum(ftpPath + DIR_SPLIT + file.getName());
                        map.put(file.getName(), String.valueOf(getFileNum(ftpPath + DIR_SPLIT + file.getName())));
                    }
                }
            }else {
                log.error(String.format("路径有误,或目录【%s】为空", ftpPath));
            }
            map.put("sum", String.valueOf(sum));
            return map;
        } catch (IOException e) {
            log.error("文件获取异常:", e);
        } finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return null;
    }


    /**
     * 下载指定文件夹到本地
     * @param ftpPath FTP服务器文件目录
     * @param localPath 下载后的文件路径
     * @param dirName 文件夹名称
     * @return
     */
    public void downloadDir(String ftpPath, String localPath, String dirName){
        FTPClient ftpClient = ftpPoolService.borrowObject();
        OutputStream outputStream = null;
        try {
            //判断是否存在该文件夹
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.getName().equals(dirName));
            if (ftpFiles != null && ftpFiles.length > 0) {
                if(ftpFiles[0].isDirectory()) {
                    // 判断本地路径目录是否存在,不存在则创建
                    File localFile = new File(localPath + DIR_SPLIT + dirName);
                    if (!localFile.exists()) {
                        localFile.mkdirs();
                    }
                    for (FTPFile file : ftpClient.listFiles(ftpPath + DIR_SPLIT + dirName)) {
                        if (file.isDirectory()) {
                            downloadDir(ftpPath + DIR_SPLIT + dirName, localPath + dirName + DIR_SPLIT, file.getName());
                        } else {
                            outputStream = Files.newOutputStream(new File(localPath + DIR_SPLIT + dirName + DIR_SPLIT + file.getName()).toPath());
                            ftpClient.retrieveFile(ftpPath + DIR_SPLIT + dirName + DIR_SPLIT + file.getName(), outputStream);
                            log.info("fileName:{},size:{}", file.getName(), file.getSize());
                            outputStream.close();
                        }
                    }
                }
            }
        }catch (Exception e){
            log.error("下载文件夹失败,filePathname:{},", ftpPath + DIR_SPLIT + dirName, e);
        }finally {
            IOUtils.closeQuietly(outputStream);
            ftpPoolService.returnObject(ftpClient);
        }
    }

        /**
     * 从本地上传文件夹
     * @param uploadPath ftp服务器地址
     * @param localPath 本地文件夹地址
     * @param dirName  文件夹名称
     * @return
     */
    public boolean uploadDir(String uploadPath, String localPath, String dirName){
        FTPClient ftpClient = ftpPoolService.borrowObject();
        try{
            // 切换到工作目录
            if (!ftpClient.changeWorkingDirectory(uploadPath)) {
                ftpClient.makeDirectory(uploadPath);
                ftpClient.changeWorkingDirectory(uploadPath);
            }
            //创建文件夹
            ftpClient.makeDirectory(uploadPath + DIR_SPLIT + dirName);
            File src = new File(localPath);
            //获取该目录下的所有文件
            File[] files = src.listFiles();
            FileInputStream input = null;
            for(File file : files) {
                if (file.isDirectory()) {
                    uploadDir(uploadPath + DIR_SPLIT + dirName, file.getAbsolutePath(), file.getName());
                } else {
                    input = new FileInputStream(file);
                    boolean storeFile = ftpClient.storeFile(uploadPath + DIR_SPLIT + dirName + DIR_SPLIT + file.getName(), input);
                    if (storeFile) {
                        log.info("文件:{}上传成功", file.getName());
                    } else {
                        throw new RuntimeException("ftp文件写入异常");
                    }
                }
            }
            if(input != null){
                input.close();
            }
        }catch (Exception e){
            log.error("文件夹上传失败",e);
        }finally {
            ftpPoolService.returnObject(ftpClient);
        }
        return false;
    }
}

总结 

到此这篇关于Java连接FTP服务器并使用ftp连接池进行文件操作指南的文章就介绍到这了,更多相关Java连接FTP服务器进行文件操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 利用Java中Calendar计算两个日期之间的天数和周数

    利用Java中Calendar计算两个日期之间的天数和周数

    Java 语言的Calendar(日历),Date(日期),和DateFormat(日期格式)组成了Java标准的一个基本但是非常重要的部分。日期是商业逻辑计算一个关键的部分。下面这篇文章就给大家介绍了如何利用Java中Calendar计算两个日期之间的天数和周数,下面来一起看看吧。
    2016-12-12
  • springboot多模块包扫描问题的解决方法

    springboot多模块包扫描问题的解决方法

    这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Java实现和电脑玩剪刀石头布游戏

    Java实现和电脑玩剪刀石头布游戏

    这篇文章主要为大家详细介绍了Java实现和电脑玩剪刀石头布游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 深入浅出Java mvc_动力节点Java学院整理

    深入浅出Java mvc_动力节点Java学院整理

    这篇文章主要为大家详细介绍了MVC的基础知识,MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • 详解Java中的mapstruct插件使用

    详解Java中的mapstruct插件使用

    mapstruct 的插件是专门用来处理 domin 实体类与 model 类的属性映射的,我们只需定义 mapper 接口,mapstruct 在编译的时候就会自动的帮我们实现这个映射接口,避免了麻烦复杂的映射实现,对Java mapstruct使用相关知识感兴趣的朋友一起看看吧
    2022-04-04
  • java查询近七日数据功能的实现

    java查询近七日数据功能的实现

    这篇文章主要介绍了java查询近七日数据功能的实现,文章内容详细,简单易懂,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2023-01-01
  • Java 关键字 volatile 的理解与正确使用

    Java 关键字 volatile 的理解与正确使用

    本文主要介绍 volatile 的使用准则,以及使用过程中需注意的地方,感兴趣的朋友一起看看吧
    2017-06-06
  • Java中@JSONField和@JsonProperty注解的用法及区别详解

    Java中@JSONField和@JsonProperty注解的用法及区别详解

    @JsonProperty和@JSONField注解都是为了解决obj转json字符串的时候,将java bean的属性名替换成目标属性名,下面这篇文章主要给大家介绍了关于Java中@JSONField和@JsonProperty注解的用法及区别的相关资料,需要的朋友可以参考下
    2024-06-06
  • Java Optional用法面试题精讲

    Java Optional用法面试题精讲

    这篇文章主要为大家介绍了Java Optional用法面试题精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Java Cache详解及简单实现

    Java Cache详解及简单实现

    这篇文章主要介绍了 Java Cache详解及简单实现的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论