springboot整合ftp连接池下载文件

一、项目结构

在这里插入图片描述

二、maven

		<!-- ftp远程工具 -->
		<dependency>
			<groupId>commons-net</groupId>
			<artifactId>commons-net</artifactId>
			<version>3.3</version>
			<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
		<dependency>
   			<groupId>com.jcraft</groupId>
   			<artifactId>jsch</artifactId>
   			<version>0.1.53</version>
		</dependency>
		<!-- pool 对象池 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
		</dependency>

三、配置文件

Ip: ip
Port: 21
UserName: admin
Password: password    
RemotePath: /test
LocalPath: /test/one
fileBackupsPath: /test/testBACKS
rootPath: /
ftpEncoding: utf-8
ftpMaxTotal: 100
ftpMinIdel: 2
ftpMaxIdle: 20
ftpMaxWaitMillis: 20000
connectTimeout: 20000

四、整合FTP连接池

4.1 FtpClientFactory

import lombok.extern.slf4j.Slf4j;
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;

import java.io.IOException;

/**
 * @author gyDeBug
 * @description:
 * @param:
 * @return:
 * @date:2021/8/24
 */
@Component
@Slf4j
public class FtpClientFactory implements PooledObjectFactory<FTPClient> {
    @Autowired
    private FtpPoolConfig config;

    //创建连接到池中
    @Override
    public PooledObject<FTPClient> makeObject() {
        FTPClient ftpClient = new FTPClient();//创建客户端实例
        return new DefaultPooledObject<>(ftpClient);
    }
    //销毁连接,当连接池空闲数量达到上限时,调用此方法销毁连接
    @Override
    public void destroyObject(PooledObject<FTPClient> pooledObject)  {
        FTPClient ftpClient = pooledObject.getObject();
        try {
            ftpClient.logout();
            if (ftpClient.isConnected()) {
                ftpClient.disconnect();
            }
        } catch (IOException e) {
            throw new RuntimeException("销毁连接:", e);
        }
    }
    //链接状态检查
    @Override
    public boolean validateObject(PooledObject<FTPClient> pooledObject) {
        FTPClient ftpClient = pooledObject.getObject();
        try {
            return ftpClient.sendNoOp();
        } catch (IOException e) {
            return false;
        }
    }
    //初始化连接
    @Override
    public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {
        FTPClient ftpClient = pooledObject.getObject();
        String host = config.getHost();
        Integer port = config.getPort();
        String username = config.getUserName();
        String password = config.getPassWord();
        String encoding = config.getEncoding();

        ftpClient.setConnectTimeout(config.getConnectTimeout());
        try {
            ftpClient.connect(host,port);
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                ftpClient.disconnect();
                log.warn("FTPServer 连接失败,replyCode: " + replyCode);
            }
            boolean login = ftpClient.login(username, password);

            if(!login){
                log.warn("ftpClient 登录失败: " +host);
            }
            ftpClient.setControlEncoding(encoding);
        }catch (Exception e){
            log.info("FTP连接或登录异常"+e.getMessage());
        }
//        ftpClient.changeWorkingDirectory(config.getWorkDir());
//        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//设置上传文件类型为二进制,否则将无法打开文件
    }
    //钝化连接,使链接变为可用状态
    @Override
    public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception {
        FTPClient ftpClient = pooledObject.getObject();
        try {
            ftpClient.changeWorkingDirectory(config.getRoot());
            ftpClient.logout();
            if (ftpClient.isConnected()) {
                ftpClient.disconnect();
            }
        } catch (IOException e) {
            throw new RuntimeException("该FTP连接不可用", e);
        }
    }
    //用于连接池中获取pool属性
    public FtpPoolConfig getConfig() {
        return config;
    }
}

4.2 FtpPool

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * @author gyDeBug
 * @description: FTP连接池
 *  *            1.可以获取池中空闲链接
 *  *            2.可以将链接归还到池中
 *  *            3.当池中空闲链接不足时,可以创建链接
 * @param:
 * @return:
 * @date:2021/8/24
 */
@Slf4j
@Component
public class FtpPool {
    FtpClientFactory factory;
    private final GenericObjectPool<FTPClient> internalPool;
    //初始化连接池
    public FtpPool(@Autowired FtpClientFactory factory){
        this.factory=factory;
        FtpPoolConfig config = factory.getConfig();
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        //是否开启空闲资源监测
        poolConfig.setTestWhileIdle(true);
        //空闲资源的检测周期(单位为毫秒)
        poolConfig.setTimeBetweenEvictionRunsMillis(60000);
        //资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除
        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
        //做空闲资源检测时,每次的采样数
        poolConfig.setNumTestsPerEvictionRun(-1);
        poolConfig.setMaxTotal(config.getMaxTotal());
        poolConfig.setMinIdle(config.getMinIdel());
        poolConfig.setMaxIdle(config.getMaxIdle());
        poolConfig.setMaxWaitMillis(config.getMaxWaitMillis());
        this.internalPool = new GenericObjectPool<FTPClient>(factory,poolConfig);

        log.info("初始化连接池成功this.factory{}",this.factory);
    }
    //从连接池中取连接
    public  FTPClient getFTPClient() {
        try {
            log.info("从连接池获取FTP连接信息,this.internalPool{}");
            Thread t = Thread.currentThread();
            log.info("FTP连接池当前线程:"+t.getName());
            return internalPool.borrowObject();
        } catch (Exception e) {
            log.info("从连接池获取FTP连接信息失败:"+e);
            e.printStackTrace();
            return null;
        }
    }
    //将链接归还到连接池
    public  void returnFTPClient(FTPClient ftpClient) {
        try {
            internalPool.returnObject(ftpClient);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 销毁池子
     */
    public  void destroy() {
        try {
            internalPool.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.3 FtpPoolConfig

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

/**
 * @author gyDeBug
 * @description:
 * @param:
 * @return:
 * @date:2021/8/24
 */
@Component
@Configuration
@Data
public class FtpPoolConfig {
    private String Host;
    private int Port;
    private String UserName;
    private String PassWord;
    private String workDir;
    private String encoding;
    private String root;
    private int MaxTotal;
    private int MinIdel;
    private int MaxIdle;
    private int MaxWaitMillis;
    private int connectTimeout;

    @Value("${config.ip}")
    public void setHost(String host) {
        Host = host;
    }
    @Value("${config.Port}")
    public void setPort(int port) {
        Port = port;
    }
    @Value("${config.UserName}")
    public void setUserName(String userName) {
        UserName = userName;
    }
    @Value("${config.Password}")
    public void setPassWord(String passWord) {
        PassWord = passWord;
    }
    @Value("${config.RemotePath}")
    public void setWorkDir(String workDir) {
        this.workDir = workDir;
    }
    @Value("${config.ftpEncoding}")
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
    @Value("${config.rootPath}")
    public void setRoot(String root) {
        this.root = root;
    }
    @Value("${config.ftpMaxTotal}")
    public void setMaxTotal(int maxTotal) {
        MaxTotal = maxTotal;
    }
    @Value("${config.ftpMinIdel}")
    public void setMinIdel(int minIdel) {
        MinIdel = minIdel;
    }
    @Value("${config.ftpMaxIdle}")
    public void setMaxIdle(int maxIdle) {
        MaxIdle = maxIdle;
    }
    @Value("${config.ftpMaxWaitMillis}")
    public void setMaxWaitMillis(int maxWaitMillis) {
        MaxWaitMillis = maxWaitMillis;
    }
    @Value("${config.connectTimeout}")
    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

}

五、 调用FTP连接池下载文件

@Service
public class FtpServiceImp implements FtpService {
    @Resource
    private FtpPool pool;/**
    *@Description: 连接池获取FTP连接信息,下载文件
    *@Param:
    *@return:
    *@Author: gyDeBug
    *@date: 2021/9/1
    **/
    @Override
    public void downFtp(){
        String remotePath = environment.getProperty("config.RemotePath").trim();
        String localPath = environment.getProperty("config.LocalPath").trim();
        String fileBackupsPath = environment.getProperty("config.fileBackupsPath").trim();
        long time1=System.currentTimeMillis();
        try{
            FTPClient ftpClient = pool.getFTPClient();
            Thread t = Thread.currentThread();
            ftpClient.enterLocalPassiveMode();
            ftpClient.changeWorkingDirectory(remotePath);
            logger.info("FTP当前线程:"+t.getName());
            logger.info("登录成功,从连接池获取FTP连接信息",ftpClient);
            logger.info("切换至工作目录" + remotePath);
            FTPFile[] fileList = ftpClient.listFiles();
//            logger.info("FTP文件列表:"+fileList.toString());
            if (fileList.length != 0) {
                for (FTPFile file : fileList) {
                    //如果是文件
                    if(file.isFile()){
                        logger.info("文件:" + file);
                        File localFile = new File(localPath + "/" + file.getName());
                        OutputStream is = new FileOutputStream(localFile);
                        ftpClient.retrieveFile(file.getName(), is);
                        logger.info("============文件名" + file.getName());
                        is.close();
                        //再下载一次进行备份
                        File fileBackups = new File(fileBackupsPath + "/" + file.getName());
                        OutputStream os = new FileOutputStream(fileBackups);
                        ftpClient.retrieveFile(file.getName(), os);
                        os.close();
                        ftpClient.deleteFile(file.getName());
                    }
                }
            }
            //归还
            pool.returnFTPClient(ftpClient);
            long time2=System.currentTimeMillis();
            logger.info("下载文件成功,FTP耗时:" + (time2 - time1) + "ms");

        }catch (Exception e) {
            logger.info("FTP下载异常:"+e);
    }

}

六、附加:FTP工具类

import cn.hutool.extra.ftp.FtpConfig;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

/**
 * @author 
 * @description:
 * @param:
 * @return:
 * @date:2021/8/24
 */
public class FtpUtil {
    @Autowired
    FtpPoolConfig config;
    @Autowired
    private ResourceLoader resourceLoader;
    @Autowired
    FtpPool pool;
    /**
     * Description: 向FTP服务器上传文件
     *
     * @Version2.0
     * @param file
     *            上传到FTP服务器上的文件
     * @return
     *           成功返回文件名,否则返回null
     */
    public  String upload(MultipartFile file) throws Exception {
        FTPClient ftpClient = pool.getFTPClient();
        //开始进行文件上传
        String fileName=UUID.randomUUID()+file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        InputStream input=file.getInputStream();
        try {
            boolean result=ftpClient.storeFile(fileName,input);//执行文件传输
            if(!result){//上传失败
                throw new RuntimeException("上传失败");
            }
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally {//关闭资源
            input.close();
            System.out.println("开始归还连接");
            pool.returnFTPClient(ftpClient);//归还资源
        }
        return fileName;
    }
    /**
     * Description: 从FTP服务器下载文件
     *
     * @Version1.0
     * @param fileName
     *        FTP服务器中的文件名
     * @param resp
     *        响应客户的响应体
     */
    public  void downLoad(String fileName, HttpServletResponse resp) throws IOException {
        FTPClient ftpClient = pool.getFTPClient();
        resp.setContentType("application/force-download");// 设置强制下载不打开 MIME
        resp.addHeader("Content-Disposition", "attachment;fileName="+fileName);// 设置文件名
        //将文件直接读取到响应体中
        OutputStream out = resp.getOutputStream();
        ftpClient.retrieveFile(config.getWorkDir()+"/"+fileName, out);
        out.flush();
        out.close();
        pool.returnFTPClient(ftpClient);
    }
    /**
     * Description: 从FTP服务器读取图片
     *
     * @Version1.0
     * @param fileName
     *        需要读取的文件名
     * @return
     *        返回文件对应的Entity
     */
    public ResponseEntity show(String fileName){
        String username=config.getUserName();
        String password=config.getPassWord();
        String host=config.getHost();
        String work=config.getWorkDir();
        //ftp://root:root@192.168.xx.xx/+fileName
        return ResponseEntity.ok(resourceLoader.getResource("ftp://"+username+":"+password+"@"+host+work+"/"+fileName));
    }

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怪异的bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值