博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
封装一个FTP工具类
阅读量:6266 次
发布时间:2019-06-22

本文共 5254 字,大约阅读时间需要 17 分钟。

封装一个FTP操作工具类

概述

前人的代码中把FTP操作和业务逻辑实现耦合在一起,据说经过多次的修改,在性能表现方面已经非常靠谱。

在原来的代码中可以看到使用了commons-net进行FTP操作,使用commons-pool对象池方式管理FTP连接,
完成了多线程下载和上传的功能,本次的修改只是把耦合的地方剥离开来。

FTP连接对象池

使用apache commons pool对象池管理方式需要提供一个工厂类,管理对象的生成销毁等。

需要实现如下方法,

PooledObject
makeObject(K key) throws Exception;void destroyObject(K key, PooledObject
p) throws Exception;boolean validateObject(K key, PooledObject
p);

apache commons pool提供了一个带泛型的接口KeyedPooledObjectFactory<K,V>

需要继承实现类提供对象工厂的key类型,及要生产的对象类型,key可以是一个类,包含FTP的IP
,端口,用户名密码等属性组成,目的是区分不同的FTP连接,

public class FtpClientConfig {        private String host;    private int port;    private String username;    private String password;...}

这里要生产的对象类型当然是FTPClient了,所以我们的工厂类是这样的,

public class FtpClientFactory implements KeyedPooledObjectFactory
{...}

相应的,我们提供一个对象池的实现,其实很简单

public class FtpClientPool extends GenericKeyedObjectPool
{ public FtpClientPool(FtpClientFactory factory, FtpPoolConfig config) { super(factory, config); }}

构造方法的参数就是我们提供的对象工厂FtpClientFactoryFtpPoolConfigFtpPoolConfig

public class FtpPoolConfig extends GenericKeyedObjectPoolConfig{    public FtpPoolConfig() {        setTestWhileIdle(true);        setTimeBetweenEvictionRunsMillis(60000);        setMinEvictableIdleTimeMillis(1800000L);        setTestOnBorrow(true);    }}

针对FTP连接设置了一些参数。

外部只要拿到FtpClientPool对象就可以获取FTPClient对象、返回FTPClien对象了,

FTPClient的生成销毁就交给了FtpClientFactory管理。

使用FTP连接对象池

FTP连接池比方数据库连接池来看,使用连接池似乎可以模仿SpringJdbcTemplate,这个模板封装了

获取连接,执行数据库操作,返还连接给连接池的过程,在这里同样也适合。这里引入一个自己实现的"模板类",

public class FtpTemplate implements FtpOperations
{ @Autowired private FtpClientPool ftpClientPool; }

继承自己定义的FTP操作接口,并且注入上一步封装好的对象池,当然在实践过程中可能做不到像JdbcTemplate

那样完全的泛型化。
比如为了泛型实例化,引入InterfaceConfig类我们才能真正实现FtpTemplate类。

public class FtpTemplate implements FtpOperations
{ ... @Override public String getFile(InterfaceConfig k, String fileName) throws Exception { if (logger.isDebugEnabled()) { logger.debug("正在下载" + toFtpInfo(k) + "/" + fileName + "文件"); } final FTPClient client = getFtpClient(getFtpClientPool(), k); boolean ret = changeDirectory(client,k); try { if(ret) { return performPerFile(client, fileName); } return null; } catch(Exception e) { logger.error("下载" + toFtpInfo(k) + "/" + fileName + "文件异常",e); throw e; } finally { //return to object pool if(client != null) { returnFtpClient(getFtpClientPool(), k, client); } } }...}

通过InterfaceConfig提供对象池识别的key获得我们需要的对象池里的对象FTPClient

private FTPClient getFtpClient(FtpClientPool ftpClientPool, InterfaceConfig k) throws Exception {        FtpClientConfig config = buildFtpClientConfig(k);        FTPClient client = null;        try {            client = ftpClientPool.borrowObject(config);        } catch (Exception e) {            logger.error("获取FTPClient对象异常 " + toFtpInfo(k),e);            throw e;        }        return client;    }    private FtpClientConfig buildFtpClientConfig(InterfaceConfig k) {        FtpClientConfig config = new FtpClientConfig();        config.setHost(k.getFtpUrl());        config.setPort(Integer.valueOf(k.getFtpPort()));        config.setUsername(k.getUserName());        config.setPassword(k.getPwd());        return config;    }

这个InterfaceConfig是业务代码中的对象类型,可能是Model类。目前为止我引入了一点点关于

业务的代码,但还没有耦合进来业务实现逻辑。

FTP工具类

其实FtpTemplate已经是一个适合业务逻辑实现的工具类的,但是它的功能单纯一些,为了完成特殊的业务功能,

如多线程下载,下载文件业务处理成功后才删除远端服务的文件等,这里再对FtpTemplate做一次封装。

public class FtpUtils {    @Autowired    private FtpTemplate ftpTemplate;    private ConcurrentHashMap
poolMap = new ConcurrentHashMap<>(); //存储线程池 public void downloadDirectory(InterfaceConfig config, final FtpCallback
callback) { logger.info("正在下载FTP目录" + toFtpInfo(config)); ThreadPoolExecutor workPool = poolMap.get(config.getInterfaceCode()); if(workPool == null) { BlockingQueue
workQueue = new LinkedBlockingQueue
(100); workPool = new ThreadPoolExecutor(MAX_CORE_NUM, MAX_THREAD_NUM, 1, TimeUnit.MINUTES, workQueue); poolMap.put(config.getInterfaceCode(), workPool); } try { List
fileNames = ftpTemplate.listFiles(config,config.getOrdersCount()); BlockingQueue
fileQueue = new LinkedBlockingQueue
(fileNames); //生产者资料 for(int i = 0; i < config.getThreadNum(); i++) { try { workPool.execute(new GetFileConsumer(config, fileQueue, callback)); } catch (Exception e) { logger.error("提交线程出现异常",e); } } } catch (Exception e) { logger.error("FTP操作出现异常"+toFtpInfo(config),e); } logger.info("下载FTP目录完成" + toFtpInfo(config)); } }

注入了FtpTemplate,加入了多线程线程池管理,下载方法也需要外部传入回调方法。

回调方法中就可以完成保存下载的FTP文件,删除远端对应的文件等逻辑。即使了多了一层多线程
下载功能的封装,我们也没有把业务处理逻辑耦合进来。当然,不满意的地方还是引入了业务的Model类。

回调操作

程序调用图

图片描述

关于单元测试

从上往下可以看出来三处封装,分别是FtpUtilsFtpTemplateFtpClientPool,我们可以分别

对他们进行单元测试,

  1. 注入FtpClientPool,测试FTP连接问题等
  2. 注入FtpTemplate,测试FTP操作问题等
  3. 注入FtpUtils,传入回调函数,测试业务问题

由于JUnit对多线程单元测试并没有提供支持,所以第3点实现起来有困难。

代码地址

见master分支

转载地址:http://kbcpa.baihongyu.com/

你可能感兴趣的文章
IntelliJ IDEA安装 一些配置
查看>>
【算法之美】求解两个有序数组的中位数 — leetcode 4. Median of Two Sorted Arrays
查看>>
post请求和get请求
查看>>
零成本实现接口自动化测试 – Java+TestNG 测试Restful service
查看>>
源码安装php时出现Sorry, I cannot run apxs. Possible reasons follow:
查看>>
使用T4模板生成POCO类
查看>>
精度 Precision
查看>>
打印内容函数
查看>>
Mina2 udp--zhengli
查看>>
组合模式
查看>>
Checked Exceptions
查看>>
Android——4.2 - 3G移植之路之 APN (五)
查看>>
用scikit-learn和pandas学习线性回归
查看>>
Effective C++ 34
查看>>
使用Logstash创建ES映射模版并进行数据默认的动态映射规则
查看>>
英文,数字和中文混合的彩色验证码实现
查看>>
由于找不到 MSVCR100.dll,无法继续执行代码
查看>>
Django中间件
查看>>
【bootstrap】bootstrap中的tooltip的使用
查看>>
Java嵌入式数据库H2学习总结
查看>>