sftp做成一个池子

前言:开发中的需求要去对方的 ftp 服务器下载文件,这里下载文件采用 ftp 方式,下载之后程序再去解析文件的内容,然后再存数据库。下载过来的文件默认是 zip 格式,需要解压 unzip 一下,然后里面有一个 csv 文件,用 easyecel 读取 csv 中的数据,至此需求梳理完成。这里每次取下载或者操作 ftp 的时候都要新建一个连接,用完再主动关闭,这里并未复用此连接,相当于说每次都是新建一个连接,这样不好,因此对这里进行设计改造。

引入 pom 依赖

        <!-- SFTP -->
        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.55</version>
        </dependency>
        <!-- commons-pool2 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.1</version>
        </dependency>

定义yml文件配置信息

sftp:
  mft:
    sftp-host: 101.233.389.58
    sftp-port: 80219
    sftp-username: user
    sftp-password: 789678
    sftp-path: /file/mft
    save-path: /mft
    pool:
     max-total: 10
     max-idle: 10
     min-idle: 5

定义配置属性SftpProperties 信息

读取yml中的属性信息,注意@ConfigurationProperties(prefix = "sftp.mft")注解是定义属性,下面的
sftpHost属性就读取到了yml文件中配置的信息,但是类中的maxTotal和yml文件中的max-total是怎么映射的呢?注意看yml中有一个pool下面 类中有一个private Pool pool = new Pool();pool改个名字就从yml文件点不进来了,说明是根据此来映射的

package x.x.config.sftpPool;

import com.jcraft.jsch.ChannelSftp;
import lombok.Data;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;


@Data
@ConfigurationProperties(prefix = "sftp.mft")
public class SftpProperties {
    private String sftpHost;
    private int sftpPort;
    private String sftpUsername;
    private String sftpPassword;
    private String sftpPath;
    private String savePath;

    private Pool pool = new Pool();
    public static class Pool extends GenericObjectPoolConfig<ChannelSftp> {
        private int maxTotal = DEFAULT_MAX_TOTAL;
        private int maxIdle = DEFAULT_MAX_IDLE;
        private int minIdle = DEFAULT_MIN_IDLE;
        public Pool() {
            super();
        }
        @Override
        public int getMaxTotal() {
            return maxTotal;
        }
        @Override
        public void setMaxTotal(int maxTotal) {
            this.maxTotal = maxTotal;
        }
        @Override
        public int getMaxIdle() {
            return maxIdle;
        }
        @Override
        public void setMaxIdle(int maxIdle) {
            this.maxIdle = maxIdle;
        }
        @Override
        public int getMinIdle() {
            return minIdle;
        }
        @Override
        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }
    }
}

定义工厂类

生产sftp连接,获取上一步定义好的属性 SftpProperties 实现三个方法,create方法创建连接

package x.x.config.sftpPool;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

import java.util.Properties;

@Data
@Slf4j
public class SftpFactory extends BasePooledObjectFactory<ChannelSftp> {
    private SftpProperties properties;
    public SftpFactory(SftpProperties properties) {
        this.properties = properties;
    }
    @Override
    public ChannelSftp create() {
        try {
            JSch jsch = new JSch();
            Session sshSession = jsch.getSession(properties.getSftpUsername(), properties.getSftpHost(), properties.getSftpPort());
            sshSession.setPassword(properties.getSftpPassword());
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            ChannelSftp channel = (ChannelSftp) sshSession.openChannel("sftp");
            channel.connect();
            return channel;
        }catch (JSchException e){
            throw new RuntimeException("连接sfpt失败", e);
        }
    }
    @Override
    public PooledObject<ChannelSftp> wrap(ChannelSftp channelSftp) {
        return new DefaultPooledObject<>(channelSftp);
    }

    @Override
    public void destroyObject(PooledObject<ChannelSftp> p) throws Exception {
        ChannelSftp channelSftp = p.getObject();
        channelSftp.disconnect();
    }
}


定义池子

引入工厂制造,此处会根据yml文件配置的属性生成连接

package x.x.config.sftpPool;

import com.jcraft.jsch.ChannelSftp;
import lombok.Data;
import org.apache.commons.pool2.impl.GenericObjectPool;

@Data
public class SftpPool {
    private GenericObjectPool<ChannelSftp> pool;

    public SftpPool(SftpFactory factory) {
        this.pool = new GenericObjectPool<>(factory, factory.getProperties().getPool());
    }
    /**
     * 获取一个sftp连接对象
     * @return sftp连接对象
     */
    public ChannelSftp borrowObject() {
        try {
            return pool.borrowObject();
        } catch (Exception e) {
            throw new RuntimeException("获取ftp连接失败", e);
        }
    }
    /**
     * 归还一个sftp连接对象
     * @param channelSftp sftp连接对象
     */
    public void returnObject(ChannelSftp channelSftp) {
        if (channelSftp!=null) {
            pool.returnObject(channelSftp);
        }
    }
}


定义help类操作

引入pool直接操作获取,用完返回

package x.x.config.sftpPool;


import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

@Slf4j
public class SftpHelper {
    private SftpPool pool;

    public SftpHelper(SftpPool pool) {
        this.pool = pool;
    }

    public void createDir(String createPath) throws Exception {
        ChannelSftp channelSftp = pool.borrowObject();
        try {
            channelSftp.cd("/");
            if (!isDirExist(createPath)) {
                String[] pathArry = createPath.split("/");
                for (String path : pathArry) {
                    if ("".equals(path)) {
                        continue;
                    }
                    if (isDirExist(path)) {
                        channelSftp.cd(path);
                    } else {
                        // 建立目录
                        channelSftp.mkdir(path);
                        // 进入并设置为当前目录
                        channelSftp.cd(path);
                    }
                }
            } else {
                channelSftp.cd(createPath);
            }
        } catch (SftpException e) {
            log.error(e.getMessage(), e);
            throw new Exception("创建路径错误:" + createPath);
        }finally {
            pool.returnObject(channelSftp);
        }
    }

    public boolean isDirExist(String directory) {
        ChannelSftp channelSftp = pool.borrowObject();
        boolean isDirExistFlag = false;
        try {
            channelSftp.cd("/");
            SftpATTRS sftpATTRS = channelSftp.lstat(directory);
            isDirExistFlag = true;
            channelSftp.cd(directory);
            return sftpATTRS.isDir();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            if ("no such file".equals(e.getMessage().toLowerCase())) {
                isDirExistFlag = false;
            }
        }finally {
            pool.returnObject(channelSftp);
        }
        return isDirExistFlag;
    }


    /**
     * 文件上传
     *
     * @param directory   目录
     * @param fileName    要上传的文件名
     */
    public void uploadFile(ChannelSftp uploadChannelSftp, String directory, String sourceFilePath, String fileName) {
        ChannelSftp channelSftp = pool.borrowObject();
        //获取输入流
        InputStream inputStream = null;
        try {
            inputStream = uploadChannelSftp.get(sourceFilePath);
        } catch (SftpException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            pool.returnObject(channelSftp);
        }
        try {
            channelSftp.cd(directory);
            //写入文件
            channelSftp.put(inputStream, fileName);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("文件上传异常:" + e.getMessage());
        } finally {
            pool.returnObject(channelSftp);
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException("Close stream error." + e.getMessage());
                }
            }
        }
    }

    /**
     * 列出目下下的所有文件
     *
     * @param sftpPath    文件路径
     * @return 文件名
     * @throws Exception 异常
     */
    public List<String> listFiles(String sftpPath) {
        ChannelSftp channelSftp = pool.borrowObject();
        Vector fileList = null;
        List<String> fileNameList = new ArrayList<>();
        try {
            fileList = channelSftp.ls(sftpPath);
        } catch (SftpException e) {
            throw new RuntimeException(e);
        }
        Iterator it = fileList.iterator();
        while (it.hasNext()) {
            String fileName = ((ChannelSftp.LsEntry) it.next()).getFilename();
            if (".".equals(fileName) || "..".equals(fileName)) {
                continue;
            }
            fileNameList.add(fileName);
        }
        pool.returnObject(channelSftp);
        return fileNameList;
    }


    /**
     * 下载文件。
     *
     * @param sourceDirectory    下载目录
     * @param downloadFile 下载的文件
     * @param targetSaveDirectory     存在本地的路径
     */
    public void downloadFile(String sourceDirectory, String downloadFile, String targetSaveDirectory) throws RuntimeException {
        ChannelSftp channelSftp = pool.borrowObject();
        OutputStream targetFile = null;
        try {
            if (sourceDirectory != null && !"".equals(sourceDirectory)) {
                channelSftp.cd(sourceDirectory);
            }
            File folder = new File(targetSaveDirectory);
            // 确保文件夹存在
            if (!folder.exists()) {
                if (!folder.mkdirs()) {
                    log.error("无法创建文件夹");
                    return;
                }
            }
            targetFile = new FileOutputStream(new File(targetSaveDirectory, downloadFile));
            channelSftp.get(downloadFile, targetFile);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            pool.returnObject(channelSftp);
            try {
                targetFile.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 删除文件
     *
     * @param directory  要删除文件所在目录
     * @param deleteFile 要删除的文件
     */
    public void deleteFile(String directory, String deleteFile) throws Exception {
        ChannelSftp channelSftp = pool.borrowObject();
        try {
            channelSftp.cd(directory);
            channelSftp.rm(deleteFile);
        } catch (Exception e) {
            throw new Exception("文件删除失败" + e.getMessage());
        }finally {
            pool.returnObject(channelSftp);
        }
    }

    /**
     * 检查文件路径是否存在
     *
     * @param path 文件路径
     * @return true/false
     */
    public boolean checkedFileDirectory(String path) {
        ChannelSftp channelSftp = pool.borrowObject();
        File file = new File(path);
        if (!file.exists() && !file.isDirectory()) {
            log.info("file or dir not exist, will create it : "+ path);
            file.setWritable(true, false);
            return file.mkdirs();
        }
        pool.returnObject(channelSftp);
        return true;
    }
}


配置bean信息

SftpFactory 工厂的建立依赖于SftpProperties 配置属性,SftpProperties 是通过注解@EnableConfigurationProperties引入

SftpPool 池子依赖于工厂SftpFactory 创建连接

sftpHelper 操作类依赖于 池子SftpPool中的连接操作具体的文件

package x.x.config.sftpPool;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(SftpProperties.class)
public class SftpConfig {
    // 工厂
    @Bean
    public SftpFactory sftpFactory(SftpProperties properties) {
        return new SftpFactory(properties);
    }

    // 连接池
    @Bean
    public SftpPool sftpPool(SftpFactory sftpFactory) {
        return new SftpPool(sftpFactory);
    }

    // 辅助类
    @Bean
    public SftpHelper sftpHelper(SftpPool sftpPool) {
        return new SftpHelper(sftpPool);
    }
}

至此一个简单的池子就完成了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值