Java Springboot整合FTPClient线程池

1、引入依赖

        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.7.0</version>
        </dependency>

2、配置FTP属性参数与支持

ftp:
  #ip
  ip: 192.168.x.x
  #端口
  port: 21
  #用户名
  username: x
  #密码
  password: x
  #初始化连接数
  initialSize: 5
  #编码格式
  encoding: UTF-8
  #缓冲区
  bufferSize: 8192
  #是否开启
  isopen: true
  #当获取ftp客户端失败后的重试次数
  retryCount: 5

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "ftp")
public class FtpProperties {

    private String  ip;
    private String  port;
    private String  username;
    private String  password;
    private Integer initialSize = 0;
    private String  encoding = "UTF-8";
    private Integer bufferSize = 4096;
    private Integer retryCount = 3;

}
public interface FtpConstants {

    //ftp文件路径编码格式
    String DEFAULT_FTP_PATH_ENCODING="ISO-8859-1";

}

3、创建FTP工厂

import com.hieasy.ftptl.properties.FtpProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;

import java.io.IOException;


/**
 * @Author  Minco
 * @Date 15:19 2021-02-19
 * @Description  ftp工厂类
 *//***/

@Slf4j
public class FtpClientPooledObjectFactory implements PooledObjectFactory<FTPClient> {

    private FtpProperties ftpProperties;

    public FtpClientPooledObjectFactory(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    @Override
    public PooledObject<FTPClient> makeObject() throws Exception {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect(ftpProperties.getIp(), Integer.valueOf(ftpProperties.getPort()));
            ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            log.info("连接ftp服务返回码:" + ftpClient.getReplyCode());
            ftpClient.setBufferSize(ftpProperties.getBufferSize());
            ftpClient.setControlEncoding(ftpProperties.getEncoding());
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            return new DefaultPooledObject<>(ftpClient);
        } catch (Exception e) {
            if (ftpClient.isAvailable()) {
                ftpClient.disconnect();
            }
            ftpClient = null;
            log.error("建立ftp连接失败!", (Object) e.getStackTrace());
            throw new Exception("建立ftp连接失败!", e);
        }
    }

    @Override
    public void destroyObject(PooledObject<FTPClient> pooledObject) throws Exception {
        FTPClient ftpClient = getObject(pooledObject);
        if (null != ftpClient && ftpClient.isConnected()) {
            ftpClient.disconnect();
        }
    }

    @Override
    public boolean validateObject(PooledObject<FTPClient> pooledObject) {
        FTPClient ftpClient = getObject(pooledObject);
        if (null == ftpClient || !ftpClient.isConnected()) {
            return false;
        }
        try {
            ftpClient.changeWorkingDirectory("/");
            return true;
        } catch (IOException e) {
            log.error("验证ftp连接失败!", (Object) e.getStackTrace());
            return false;
        }
    }

    @Override
    public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {

    }

    @Override
    public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception {

    }

    private FTPClient getObject(PooledObject<FTPClient> pooledObject) {
        if (null == pooledObject || null == pooledObject.getObject()) {
            return null;
        }
        return pooledObject.getObject();
    }
}

4、引入FTP配置

import com.hieasy.ftptl.factory.FtpClientPooledObjectFactory;
import com.hieasy.ftptl.properties.FtpProperties;
import com.hieasy.ftptl.service.FtpProcessor;
import com.hieasy.ftptl.service.impl.DefaultFtpProcessor;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;


@Configuration
@ConditionalOnClass({GenericObjectPool.class, FTPClient.class})
@ConditionalOnProperty(prefix = "ftp", name = "isopen", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(FtpProperties.class)
public class FtpConfig {

    private Logger log = LoggerFactory.getLogger(FtpConfig.class);

    private final FtpProperties ftpProperties;

    @Autowired
    public FtpConfig(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    private ObjectPool<FTPClient> pool;

    /**
     * 预加载FTPClient连接到对象池中
     *
     * @param initialSize
     * @param maxIdle
     */
    private void preLoadingFtpClient(Integer initialSize, int maxIdle) {
        //如果初始化大小为null或者小于等于0,则不执行逻辑
        if (null == initialSize || initialSize <= 0) {
            return;
        }
        int size = Math.min(initialSize, maxIdle);
        try {
            for (int i = 0; i < size; i++) {
                pool.addObject();
            }
        } catch (Exception e) {
            log.error("预加载失败!", (Object) e.getStackTrace());
        }
    }

    /**
     * 销毁方法
     */
    @PreDestroy
    public void destroy() {
        if (null != pool) {
            pool.close();
            log.info("销毁ftp客户端连接池。。。");
        }
    }

    /**
     * 判断不存在业务Service时初始化默认Bean到Spring
     */
    @Bean
    @ConditionalOnMissingBean(FtpProcessor.class)
    public FtpProcessor ftpProcessor() {
        log.info("没有找到ftp处理器,执行创建默认处理器");
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        poolConfig.setTestWhileIdle(true);
        poolConfig.setMinEvictableIdleTimeMillis(6000);
        poolConfig.setSoftMinEvictableIdleTimeMillis(50000);
        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
        pool = new GenericObjectPool<>(new FtpClientPooledObjectFactory(ftpProperties), poolConfig);
        preLoadingFtpClient(ftpProperties.getInitialSize(), poolConfig.getMaxIdle());
        DefaultFtpProcessor processor = new DefaultFtpProcessor(ftpProperties);
        processor.setFtpClientPool(pool);
        processor.setHasInit(true);
        return processor;
    }
}

5、书写处理工具类

import org.apache.commons.net.ftp.FTPClient;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public interface FtpProcessor {

    boolean uploadFile(String path, String fileName, String originFileName);

    boolean uploadFile(String path, String fileName, InputStream inputStream) throws IOException;

    boolean downloadFile(String path, String fileName, String localPath);

    boolean deleteFile(String path, String fileName);

    boolean createDirectory(String remote, FTPClient ftpClient) throws IOException;

    boolean existFile(String path, FTPClient ftpClient) throws IOException;

    boolean makeDirectory(String directory, FTPClient ftpClient);

    List<String> retrieveFileNames(String remotePath) throws IOException;



}
import com.hieasy.ftptl.properties.FtpProperties;
import com.hieasy.ftptl.service.FtpProcessor;
import com.hieasy.ftptl.support.FtpConstants;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.pool2.ObjectPool;
import org.springframework.util.Assert;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


@Getter
@Setter
@Slf4j
public class DefaultFtpProcessor implements FtpProcessor {

    private final FtpProperties ftpProperties;

    //连接池初始化标志
    private boolean hasInit = false;
    //连接池
    private ObjectPool<FTPClient> ftpClientPool;

    public DefaultFtpProcessor(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    /**
     * 上传文件
     *
     * @param path           ftp服务器保存地址
     * @param fileName       上传到ftp的文件名
     * @param originFileName 等待上传的文件名(绝对地址或路径)
     */
    @Override
    public boolean uploadFile(String path, String fileName, String originFileName) {
        boolean flag = false;
        try {
            InputStream inputStream  = new FileInputStream(new File(originFileName));
            flag=uploadFile( path,  fileName,  inputStream);
        } catch (Exception e) {
            //log.error("上传文件出错!", (Object) e.getStackTrace());
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 上传文件
     *
     * @param path        ftp服务器保存地址
     * @param fileName    上传到ftp的文件名
     * @param inputStream 文件流
     */
    @Override
    public boolean uploadFile(String path, String fileName, InputStream inputStream) throws IOException {
        boolean flag = false;
        FTPClient ftpClient = getFtpClient();
        try {
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            createDirectory(path, ftpClient);
            ftpClient.makeDirectory(path);
            ftpClient.changeWorkingDirectory(path);
            ftpClient.storeFile(fileName, inputStream);
            flag = true;
        } catch (Exception e) {
            //log.error("上传文件出错!:{}", e);
            e.printStackTrace();
        } finally {
            if(null!=inputStream) inputStream.close();
            releaseFtpClient(ftpClient);
        }
        return flag;
    }

    /**
     * 下载文件
     *
     * @param path      ftp服务器文件路径
     * @param fileName  文件名称
     * @param localPath 下载后的路径
     */
    @Override
    public boolean downloadFile(String path, String fileName, String localPath) {
        boolean flag = false;
        OutputStream outputStream = null;
        FTPClient ftpClient = getFtpClient();
        try {
            ftpClient.changeWorkingDirectory(path);
            FTPFile[] files = ftpClient.listFiles();
            for (FTPFile file : files) {
                if (fileName.equalsIgnoreCase(file.getName())) {
                    File localFile = new File(localPath + "/" + file.getName());
                    outputStream = new FileOutputStream(localFile);
                    ftpClient.retrieveFile(file.getName(), outputStream);
                    outputStream.close();
                }
            }
            flag = true;
        } catch (IOException e) {
            log.error("下载文件出错!", (Object) e.getStackTrace());
        } finally {
            releaseFtpClient(ftpClient);
            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("关闭输出流出错!", (Object) e.getStackTrace());
                }
            }
        }
        return flag;
    }

    /**
     * 删除文件
     *
     * @param path     ftp文件路径
     * @param fileName 文件名
     */
    @Override
    public boolean deleteFile(String path, String fileName) {
        boolean flag = false;
        FTPClient ftpClient = getFtpClient();
        try {
            ftpClient.changeWorkingDirectory(path);
            ftpClient.dele(fileName);
            ftpClient.logout();
            flag = true;
        } catch (IOException e) {
            log.error("删除文件出错!", (Object) e.getStackTrace());
        } finally {
            releaseFtpClient(ftpClient);
        }
        return flag;
    }

    /**
     * 创建多层目录,如果ftp服务器已存在该目录,则不创建,如果没有,则创建
     *
     * @param remote    创建的目录
     * @param ftpClient
     */
    @Override
    public boolean createDirectory(String remote, FTPClient ftpClient) throws IOException {
        String directory = remote + "/";
        //如果远程目录不存在,则递归创建远程目录
        if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(directory, ftpClient)) {
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            }
            end = directory.indexOf("/", start);
            String path = "";
            String paths = "";
            do {
                String subDirectory = new String(remote.substring(start, end).getBytes(ftpProperties.getEncoding()), FtpConstants.DEFAULT_FTP_PATH_ENCODING);
                path = path + "/" + subDirectory;
                if (!existFile(path, ftpClient)) {
                    if (makeDirectory(subDirectory, ftpClient)) {
                        changeWorkingDirectory(subDirectory, ftpClient);
                    } else {
                        log.warn("创建目录[" + subDirectory + "]失败");
                        changeWorkingDirectory(subDirectory, ftpClient);
                    }
                } else {
                    changeWorkingDirectory(subDirectory, ftpClient);
                }
                paths = paths + "/" + subDirectory;
                start = end + 1;
                end = directory.indexOf("/", start);
            } while (end <= start);
        }
        return true;
    }

    /**
     * 判断ftp服务器的路径或文件是否存在
     *
     * @param path
     * @param ftpClient
     */
    @Override
    public boolean existFile(String path, FTPClient ftpClient) throws IOException {
        boolean flag = false;
        FTPFile[] files = ftpClient.listFiles(path);
        if (files.length > 0) {
            flag = true;
        }
        return flag;
    }

    /**
     * 创建目录
     *
     * @param directory
     * @param ftpClient
     */
    @Override
    public boolean makeDirectory(String directory, FTPClient ftpClient) {
        boolean flag = true;
        try {
            flag = ftpClient.makeDirectory(directory);
            if (flag) {
                log.info("创建文件夹:" + directory);
            }
        } catch (IOException e) {
            log.error("创建文件夹" + directory + "失败!", (Object) e.getStackTrace());
        }
        return flag;
    }

    /**
     * 切换目录
     *
     * @param directory 要切换的目录
     * @param ftpClient ftp客户端
     */
    public boolean changeWorkingDirectory(String directory, FTPClient ftpClient) {
        boolean flag = true;
        try {
            flag = ftpClient.changeWorkingDirectory(directory);
            if (flag) {
                log.info("进入文件夹:" + directory);
            }
        } catch (IOException e) {
            log.error("进入文件夹:"+directory+"错误!", (Object) e.getStackTrace());
        }
        return flag;
    }

    /**
     * 按行读取FTP文件
     *
     * @param remoteFilePath ftp路径
     */
    public List<String> readFileByLine(String remoteFilePath) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try (InputStream inputStream = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            return reader.lines()
                    .map(line -> StringUtils.trimToEmpty(line))
                    .filter(line -> StringUtils.isNotEmpty(line))
                    .collect(Collectors.toList());
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 获取指定路径下的ftp文件
     *
     * @param remotePath 指定路径
     */
    public FTPFile[] retrieveFtpFiles(String remotePath) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try {
            return ftpClient.listFiles(encodingPath(remotePath + "/"), file -> file != null && file.getSize() > 0);
        } finally {
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 获取指定ftp路径下的文件名称
     *
     * @param remotePath 指定ftp路径
     */
    @Override
    public List<String> retrieveFileNames(String remotePath) throws IOException {
        FTPFile[] files = retrieveFtpFiles(remotePath);
        if (ArrayUtils.isEmpty(files)) {
            return new ArrayList<>();
        }
        return Arrays.stream(files).filter(Objects::nonNull).map(FTPFile::getName).collect(Collectors.toList());
    }

    /**
     * 获取编码后的文件路径
     */
    private String encodingPath(String path) throws UnsupportedEncodingException {
        //在FTP协议中,规定文件名编码格式为ISO-8859-1,所以目录名或文件名需要转码
        return new String(path.replaceAll("//", "/").getBytes(ftpProperties.getEncoding()), FtpConstants.DEFAULT_FTP_PATH_ENCODING);
    }

    /**
     * 获取ftp客户端
     */
    private FTPClient getFtpClient() {
        checkFtpClientPoolAvailable();
        FTPClient ftpClient = null;
        Exception exception = null;
        //获取连接,做多尝试n次
        try {
            for (int i = 0; i < ftpProperties.getRetryCount(); i++) {
                ftpClient = ftpClientPool.borrowObject();
                ftpClient.enterLocalPassiveMode();//设置为被动模式
                ftpClient.changeWorkingDirectory("/");
                break;
            }
        } catch (Exception e) {
            log.error("无法在连接池中获取ftp客户端!", (Object) e.getStackTrace());
            exception = e;
        }
        if (null == ftpClient) {
            throw new RuntimeException("无法在连接池中获取ftp客户端", exception);
        }
        return ftpClient;
    }

    /**
     * 释放ftp客户端
     *
     * @param ftpClient
     */
    private void releaseFtpClient(FTPClient ftpClient) {
        if (null != ftpClient) {
            try {
                //从ftp连接池中移除ftp客户端
                ftpClientPool.returnObject(ftpClient);
            } catch (Exception e) {
                try {
                    //判断客户端是否可用
                    if (ftpClient.isAvailable()) {
                        //销毁连接
                        ftpClient.disconnect();
                    }
                } catch (IOException ex) {
                    log.error("销毁ftp连接失败!", (Object) e.getStackTrace());
                }
                log.error("从ftp连接池移除ftp客户端失败!", (Object) e.getStackTrace());
            }
        }
    }

    /**
     * 检查ftp连接池是否可用
     */
    private void checkFtpClientPoolAvailable() {
        Assert.state(hasInit, "ftp未启用或连接失败!");
    }
}

6、调用测试

import com.hieasy.ftptl.service.FtpProcessor;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@Slf4j
@RestController
@RequestMapping("/comm")
public class CommCtrl {


    @Autowired
    private FtpProcessor ftpProcessor;


    @GetMapping("test")
    @ApiOperation(value = "测试",notes = "q")
    public String test() throws Exception {
        boolean  b1= ftpProcessor.uploadFile("/Inventory", "zw2.txt", "C:/Users/Minco/Desktop/图片/中文.txt");
        boolean  b2= ftpProcessor.uploadFile("/Sellout", "zw3.txt", "C:/Users/Minco/Desktop/图片/中文.txt");
        return "上传结果B1:"+b1+",B2:"+b2;
    }


}

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
springboot整合ftp可以通过引入相关的依赖和配置来实现。首先,在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency> ``` 然后,在application.properties或application.yml文件中配置ftp连接信息,例如: ``` ftp.host=ftp.example.com ftp.port=21 ftp.username=admin ftp.password=123456 ``` 接下来,创建一个FTP工具类,用于连接和操作ftp服务器。在该类中,可以使用`FTPClient`类进行ftp操作,例如上传文件、下载文件等。示例如下: ```java import org.apache.commons.net.ftp.FTPClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class FTPUtil { @Value("${ftp.host}") private String host; @Value("${ftp.port}") private int port; @Value("${ftp.username}") private String username; @Value("${ftp.password}") private String password; public void uploadFile(String remotePath, String fileName, File file) throws IOException { FTPClient ftpClient = new FTPClient(); try { ftpClient.connect(host, port); ftpClient.login(username, password); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); ftpClient.enterLocalPassiveMode(); ftpClient.changeWorkingDirectory(remotePath); try (InputStream inputStream = new FileInputStream(file)) { ftpClient.storeFile(fileName, inputStream); } } finally { if (ftpClient.isConnected()) { ftpClient.disconnect(); } } } // 其他ftp操作方法... } ``` 最后,可以在其他地方注入`FTPUtil`类,并调用其方法来实现ftp操作。例如: ```java @Service public class MyService { private final FTPUtil ftpUtil; @Autowired public MyService(FTPUtil ftpUtil) { this.ftpUtil = ftpUtil; } public void uploadFile() { // 上传文件 File file = new File("path/to/file"); try { ftpUtil.uploadFile("remote/path", "filename.txt", file); } catch (IOException e) { e.printStackTrace(); } } // 其他方法... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值