jianghuchuanke 2019-09-13
commons-net 是 apachecommons 用于网络的工具包,它实现了一些常见的网络工具,如 smtp
,pop3
,telnet
,ftp
,udp
等,本文主要使用它的 ftp 工具。
在使用 commons-net 提供的 ftp 工具的时候 ,发现每次都要走一遍完整的连接,登录流程。每次都创建连接的话,很快就会把连接耗光,如果使用单例,则效率过低,因为只有一个连接,所以考虑用对象池的方式。
自已定义对象池的话,我之前有弄过,但要考虑好多的问题。像线程池一样,需要考虑核心对象数、最大对象数、何时创建对象 、及队列等,这时可以使用 apache 的 commons-pool2 来做一个对象池。
可以这么想,如果我要做个对象池的工具给别人用,首先要考虑的是池子里装的什么,用户要如何创建池子里的对象,然后提供方法借对象和回收对象,我可以把池子中的对象抽象出来,由一个工厂统一管理,用户的对象从我的通用对象继承或实现,或使用聚合方式 。
其实 spring-data-redis 已经给我们一个完整的使用 commons-pool2 的例子,它就是用的 commons-pool2 ,它的池中对象是 redis 连接,有兴趣可以去瞧瞧。
因为 FTPClient 的功能太过简单,连多层目录时自己创建目录都不会,所以有必要给它包装一下,这里你可以扩展常用到的方法。
import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; @Slf4j public class FtpClientExtend { private FTPClient ftpClient ; public FtpClientExtend(FTPClient ftpClient) { this.ftpClient = ftpClient; } /** * 列出文件列表 * @param filePath * @return * @throws IOException */ public FTPFile[] listFiles(String filePath) throws IOException { return ftpClient.listFiles(filePath); } /** * 下载文件 * @param filePath * @return */ public InputStream downloadFile(String filePath) throws IOException { return ftpClient.retrieveFileStream(filePath); } /** * 存储文件 * @param s * @param inputStream */ public void uploadFile(String filePath, InputStream inputStream) throws IOException { File targetFilePath = new File(filePath); Path path = targetFilePath.getParentFile().toPath(); Iterator<Path> iterator = path.iterator(); StringBuffer root = new StringBuffer(""); while (iterator.hasNext()){ Path next = iterator.next(); root.append("/").append(next); //尝试切入目录 boolean success = ftpClient.changeWorkingDirectory(root.toString()); if(!success){ int mkd = ftpClient.mkd(next.toString()); ftpClient.changeWorkingDirectory(root.toString()); } } ftpClient.enterLocalPassiveMode(); ftpClient.setControlEncoding("UTF-8"); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); boolean storeFileResult = ftpClient.storeFile(targetFilePath.getName(), inputStream); if (storeFileResult) { log.debug("上传文件:" + filePath + ",到目录:" + ftpClient.printWorkingDirectory() + " 成功"); }else{ log.debug("上传文件:" + filePath + ",到目录:" + ftpClient.printWorkingDirectory() + " 失败"); } } }
这个包裹对象的类才是工厂真正产生在池中的类,文末给出图示
import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public class FtpClientPool extends GenericObjectPool<FtpClientExtend> { public FtpClientPool(PooledObjectFactory<FtpClientExtend> factory) { super(factory); } public FtpClientPool(PooledObjectFactory<FtpClientExtend> factory, GenericObjectPoolConfig config) { super(factory, config); } }
import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; public class FtpClientFactory extends BasePooledObjectFactory<FtpClientExtend> { @Value("${ftp.host:localhost}") private String host; @Value("${ftp.port:21}") private int port; @Value("${ftp.username:ftpadmin}") private String username; @Value("${ftp.password:salt202}") private String password; @Override public FtpClientExtend create() throws Exception { FTPClient ftpClient = new FTPClient(); ftpClient.connect(host,port); boolean login = ftpClient.login(username, password); if(!login){ throw new RuntimeException("ftp 登录失败,检查用户名密码是否正确["+host+":"+port+"]["+username+"]["+password+"]"); } return new FtpClientExtend(ftpClient); } @Override public PooledObject<FtpClientExtend> wrap(FtpClientExtend ftpClientExtend) { return new DefaultPooledObject(ftpClientExtend); } }
@Autowired private FtpClientPool ftpClientPool; public void method(){ FtpClientExtend ftpClientExtend = null; try{ ftpClientExtend = ftpClientPool.borrowObject(); }finally{ if(ftpClientExtend != null) { ftpClientPool.returnObject(ftpClientExtend); } } }