经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Spring Boot整合FTPClient线程池的实现示例
来源:jb51  时间:2018/12/24 10:43:39  对本文有异议

最近在写一个FTP上传工具,用到了Apache的FTPClient,但是每个线程频繁的创建和销毁FTPClient对象对服务器的压力很大,因此,此处最好使用一个FTPClient连接池。仔细翻了一下Apache的api,发现它并没有一个FTPClientPool的实现,所以,不得不自己写一个FTPClientPool。下面就大体介绍一下开发连接池的整个过程,供大家参考。

我们可以利用Apache提供的common-pool包来协助我们开发连接池。而开发一个简单的对象池,仅需要实现common-pool 包中的ObjectPool和PoolableObjectFactory两个接口即可。

线程池的意义

为了减少频繁创建、销毁对象带来的性能消耗,我们可以利用对象池的技术来实现对象的复用。对象池提供了一种机制,它可以管理对象池中对象的生命周期,提供了获取和释放对象的方法,可以让客户端很方便的使用对象池中的对象。

pom引入依赖

  1. <!-- FtpClient依赖包-->
  2. <dependency>
  3. <groupId>commons-net</groupId>
  4. <artifactId>commons-net</artifactId>
  5. <version>3.5</version>
  6. </dependency>
  7.  
  8. <!-- 线程池-->
  9. <dependency>
  10. <groupId>commons-pool</groupId>
  11. <artifactId>commons-pool</artifactId>
  12. <version>1.6</version>
  13. </dependency>
  14.  
  15. <dependency>
  16. <groupId>org.apache.commons</groupId>
  17. <artifactId>commons-pool2</artifactId>
  18. <version>2.0</version>
  19. </dependency>

创建ftp配置信息

在resources目录下创建ftp.properties配置文件,目录结构如下:

添加如下的配置信息:

  1. ########### FTP用户名称 ###########
  2. ftp.userName=hrabbit
  3. ########### FTP用户密码 ###########
  4. ftp.passWord=123456
  5. ########### FTP主机IP ###########
  6. ftp.host=127.0.0.1
  7. ########### FTP主机端口号 ###########
  8. ftp.port=21
  9. ########### 保存根路径 ###########
  10. ftp.baseUrl=/

创建FTPProperties.java配置文件

加载配置内容到Spring中,配置信息基本延用我的就可以。

  1. /**
  2. * FTP的配置信息
  3. * @Auther: hrabbit
  4. * @Date: 2018-12-03 2:06 PM
  5. * @Description:
  6. */
  7. @Data
  8. @Component
  9. @PropertySource("classpath:ftp.properties")
  10. @ConfigurationProperties(prefix = "ftp")
  11. public class FTPProperties {
  12.  
  13. private String username;
  14.  
  15. private String password;
  16.  
  17. private String host;
  18.  
  19. private Integer port;
  20.  
  21. private String baseUrl;
  22.  
  23. private Integer passiveMode = FTP.BINARY_FILE_TYPE;
  24.  
  25. private String encoding="UTF-8";
  26.  
  27. private int clientTimeout=120000;
  28.  
  29. private int bufferSize;
  30.  
  31. private int transferFileType=FTP.BINARY_FILE_TYPE;
  32.  
  33. private boolean renameUploaded;
  34.  
  35. private int retryTime;
  36. }

创建FTPClientPool线程池

  1. /**
  2. * 自定义实现ftp连接池
  3. * @Auther: hrabbit
  4. * @Date: 2018-12-03 3:40 PM
  5. * @Description:
  6. */
  7. @Slf4j
  8. @SuppressWarnings("all")
  9. public class FTPClientPool implements ObjectPool<FTPClient> {
  10.  
  11. private static final int DEFAULT_POOL_SIZE = 10;
  12.  
  13. public BlockingQueue<FTPClient> blockingQueue;
  14.  
  15. private FTPClientFactory factory;
  16.  
  17. public FTPClientPool(FTPClientFactory factory) throws Exception {
  18. this(DEFAULT_POOL_SIZE, factory);
  19. }
  20.  
  21. public FTPClientPool(int poolSize, FTPClientFactory factory) throws Exception {
  22. this.factory = factory;
  23. this.blockingQueue = new ArrayBlockingQueue<FTPClient>(poolSize);
  24. initPool(poolSize);
  25. }
  26.  
  27. /**
  28. * 初始化连接池
  29. * @param maxPoolSize
  30. * 最大连接数
  31. * @throws Exception
  32. */
  33. private void initPool(int maxPoolSize) throws Exception {
  34. int count = 0;
  35. while(count < maxPoolSize) {
  36. this.addObject();
  37. count++;
  38. }
  39. }
  40.  
  41. /**
  42. * 从连接池中获取对象
  43. */
  44. @Override
  45. public FTPClient borrowObject() throws Exception {
  46. FTPClient client = blockingQueue.take();
  47. if(client == null) {
  48. client = factory.makeObject();
  49. } else if(!factory.validateObject(client)) {
  50. invalidateObject(client);
  51. client = factory.makeObject();
  52. }
  53. return client;
  54. }
  55.  
  56. /**
  57. * 返还一个对象(链接)
  58. */
  59. @Override
  60. public void returnObject(FTPClient client) throws Exception {
  61. if ((client != null) && !blockingQueue.offer(client,2,TimeUnit.MINUTES)) {
  62. try {
  63. factory.destroyObject(client);
  64. } catch (Exception e) {
  65. throw e;
  66. }
  67. }
  68. }
  69.  
  70. /**
  71. * 移除无效的对象(FTP客户端)
  72. */
  73. @Override
  74. public void invalidateObject(FTPClient client) throws Exception {
  75. blockingQueue.remove(client);
  76. }
  77.  
  78. /**
  79. * 增加一个新的链接,超时失效
  80. */
  81. @Override
  82. public void addObject() throws Exception {
  83. blockingQueue.offer(factory.makeObject(), 2, TimeUnit.MINUTES);
  84. }
  85.  
  86. /**
  87. * 重新连接
  88. */
  89. public FTPClient reconnect() throws Exception {
  90. return factory.makeObject();
  91. }
  92.  
  93. /**
  94. * 获取空闲链接数(这里暂不实现)
  95. */
  96. @Override
  97. public int getNumIdle() {
  98. return blockingQueue.size();
  99. }
  100.  
  101. /**
  102. * 获取正在被使用的链接数
  103. */
  104. @Override
  105. public int getNumActive() {
  106. return DEFAULT_POOL_SIZE - getNumIdle();
  107. }
  108.  
  109. @Override
  110. public void clear() throws Exception {
  111.  
  112. }
  113.  
  114. /**
  115. * 关闭连接池
  116. */
  117. @Override
  118. public void close() {
  119. try {
  120. while(blockingQueue.iterator().hasNext()) {
  121. FTPClient client = blockingQueue.take();
  122. factory.destroyObject(client);
  123. }
  124. } catch(Exception e) {
  125. log.error("close ftp client pool failed...{}", e);
  126. }
  127. }
  128.  
  129. /**
  130. * 增加一个新的链接,超时失效
  131. */
  132. public void addObject(FTPClient ftpClient) throws Exception {
  133. blockingQueue.put(ftpClient);
  134. }
  135. }

创建一个FTPClientFactory工厂类

创建FTPClientFactory实现PoolableObjectFactory的接口,FTPClient工厂类,通过FTPClient工厂提供FTPClient实例的创建和销毁

  1. /**
  2. * FTPClient 工厂
  3. * @Auther: hrabbit
  4. * @Date: 2018-12-03 3:41 PM
  5. * @Description:
  6. */
  7. @Slf4j
  8. @SuppressWarnings("all")
  9. public class FTPClientFactory implements PoolableObjectFactory<FTPClient> {
  10.  
  11. private FTPProperties ftpProperties;
  12.  
  13. public FTPClientFactory(FTPProperties ftpProperties) {
  14. this.ftpProperties = ftpProperties;
  15. }
  16.  
  17. @Override
  18. public FTPClient makeObject() throws Exception {
  19. FTPClient ftpClient = new FTPClient();
  20. ftpClient.setControlEncoding(ftpProperties.getEncoding());
  21. ftpClient.setConnectTimeout(ftpProperties.getClientTimeout());
  22. try {
  23. ftpClient.connect(ftpProperties.getHost(), ftpProperties.getPort());
  24. int reply = ftpClient.getReplyCode();
  25. if (!FTPReply.isPositiveCompletion(reply)) {
  26. ftpClient.disconnect();
  27. log.warn("FTPServer refused connection");
  28. return null;
  29. }
  30. boolean result = ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
  31. ftpClient.setFileType(ftpProperties.getTransferFileType());
  32. if (!result) {
  33. log.warn("ftpClient login failed... username is {}", ftpProperties.getUsername());
  34. }
  35. } catch (Exception e) {
  36. log.error("create ftp connection failed...{}", e);
  37. throw e;
  38. }
  39.  
  40. return ftpClient;
  41. }
  42.  
  43. @Override
  44. public void destroyObject(FTPClient ftpClient) throws Exception {
  45. try {
  46. if(ftpClient != null && ftpClient.isConnected()) {
  47. ftpClient.logout();
  48. }
  49. } catch (Exception e) {
  50. log.error("ftp client logout failed...{}", e);
  51. throw e;
  52. } finally {
  53. if(ftpClient != null) {
  54. ftpClient.disconnect();
  55. }
  56. }
  57.  
  58. }
  59.  
  60. @Override
  61. public boolean validateObject(FTPClient ftpClient) {
  62. try {
  63. return ftpClient.sendNoOp();
  64. } catch (Exception e) {
  65. log.error("Failed to validate client: {}");
  66. }
  67. return false;
  68. }
  69.  
  70. @Override
  71. public void activateObject(FTPClient obj) throws Exception {
  72. //Do nothing
  73.  
  74. }
  75.  
  76. @Override
  77. public void passivateObject(FTPClient obj) throws Exception {
  78. //Do nothing
  79.  
  80. }
  81. }

创建FTPUtils.java的工具类

FTPUtils.java中封装了上传、下载等方法,在项目启动的时候,在@PostConstruct注解的作用下通过执行init()的方法,创建FTPClientFactory工厂中,并初始化了FTPClientPool线程池,这样每次调用方法的时候,都直接从FTPClientPool中取出一个FTPClient对象

  1. /**
  2. * @Auther: hrabbit
  3. * @Date: 2018-12-03 3:47 PM
  4. * @Description:
  5. */
  6. @Slf4j
  7. @Component
  8. public class FTPUtils {
  9.  
  10. /**
  11. * FTP的连接池
  12. */
  13. @Autowired
  14. public static FTPClientPool ftpClientPool;
  15. /**
  16. * FTPClient对象
  17. */
  18. public static FTPClient ftpClient;
  19.  
  20.  
  21. private static FTPUtils ftpUtils;
  22.  
  23. @Autowired
  24. private FTPProperties ftpProperties;
  25.  
  26. /**
  27. * 初始化设置
  28. * @return
  29. */
  30. @PostConstruct
  31. public boolean init() {
  32. FTPClientFactory factory = new FTPClientFactory(ftpProperties);
  33. ftpUtils = this;
  34. try {
  35. ftpClientPool = new FTPClientPool(factory);
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. return false;
  39. }
  40. return true;
  41. }
  42.  
  43.  
  44. /**
  45. * 获取连接对象
  46. * @return
  47. * @throws Exception
  48. */
  49. public static FTPClient getFTPClient() throws Exception {
  50. //初始化的时候从队列中取出一个连接
  51. if (ftpClient==null) {
  52. synchronized (ftpClientPool) {
  53. ftpClient = ftpClientPool.borrowObject();
  54. }
  55. }
  56. return ftpClient;
  57. }
  58.  
  59.  
  60. /**
  61. * 当前命令执行完成命令完成
  62. * @throws IOException
  63. */
  64. public void complete() throws IOException {
  65. ftpClient.completePendingCommand();
  66. }
  67.  
  68. /**
  69. * 当前线程任务处理完成,加入到队列的最后
  70. * @return
  71. */
  72. public void disconnect() throws Exception {
  73. ftpClientPool.addObject(ftpClient);
  74. }
  75.  
  76. /**
  77. * Description: 向FTP服务器上传文件
  78. *
  79. * @Version1.0
  80. * @param remoteFile
  81. * 上传到FTP服务器上的文件名
  82. * @param input
  83. * 本地文件流
  84. * @return 成功返回true,否则返回false
  85. */
  86. public static boolean uploadFile(String remoteFile, InputStream input) {
  87. boolean result = false;
  88. try {
  89. getFTPClient();
  90. ftpClient.enterLocalPassiveMode();
  91. result = ftpClient.storeFile(remoteFile, input);
  92. input.close();
  93. ftpClient.disconnect();
  94. } catch (Exception e) {
  95. e.printStackTrace();
  96. }
  97. return result;
  98. }
  99.  
  100. /**
  101. * Description: 向FTP服务器上传文件
  102. *
  103. * @Version1.0
  104. * @param remoteFile
  105. * 上传到FTP服务器上的文件名
  106. * @param localFile
  107. * 本地文件
  108. * @return 成功返回true,否则返回false
  109. */
  110. public static boolean uploadFile(String remoteFile, String localFile){
  111. FileInputStream input = null;
  112. try {
  113. input = new FileInputStream(new File(localFile));
  114. } catch (FileNotFoundException e) {
  115. e.printStackTrace();
  116. }
  117. return uploadFile(remoteFile, input);
  118. }
  119.  
  120. /**
  121. * 拷贝文件
  122. * @param fromFile
  123. * @param toFile
  124. * @return
  125. * @throws Exception
  126. */
  127. public boolean copyFile(String fromFile, String toFile) throws Exception {
  128. InputStream in=getFileInputStream(fromFile);
  129. getFTPClient();
  130. boolean flag = ftpClient.storeFile(toFile, in);
  131. in.close();
  132. return flag;
  133. }
  134.  
  135. /**
  136. * 获取文件输入流
  137. * @param fileName
  138. * @return
  139. * @throws IOException
  140. */
  141. public static InputStream getFileInputStream(String fileName) throws Exception {
  142. ByteArrayOutputStream fos=new ByteArrayOutputStream();
  143. getFTPClient();
  144. ftpClient.retrieveFile(fileName, fos);
  145. ByteArrayInputStream in=new ByteArrayInputStream(fos.toByteArray());
  146. fos.close();
  147. return in;
  148. }
  149.  
  150. /**
  151. * Description: 从FTP服务器下载文件
  152. *
  153. * @Version1.0
  154. * @return
  155. */
  156. public static boolean downFile(String remoteFile, String localFile){
  157. boolean result = false;
  158. try {
  159. getFTPClient();
  160. OutputStream os = new FileOutputStream(localFile);
  161. ftpClient.retrieveFile(remoteFile, os);
  162. ftpClient.logout();
  163. ftpClient.disconnect();
  164. result = true;
  165. } catch (Exception e) {
  166. e.printStackTrace();
  167. } finally {
  168. try {
  169. } catch (Exception e) {
  170. e.printStackTrace();
  171. }
  172. }
  173. return result;
  174. }
  175.  
  176. /**
  177. * 从ftp中获取文件流
  178. * @param filePath
  179. * @return
  180. * @throws Exception
  181. */
  182. public static InputStream getInputStream(String filePath) throws Exception {
  183. getFTPClient();
  184. InputStream inputStream = ftpClient.retrieveFileStream(filePath);
  185. return inputStream;
  186. }
  187.  
  188. /**
  189. * ftp中文件重命名
  190. * @param fromFile
  191. * @param toFile
  192. * @return
  193. * @throws Exception
  194. */
  195. public boolean rename(String fromFile,String toFile) throws Exception {
  196. getFTPClient();
  197. boolean result = ftpClient.rename(fromFile,toFile);
  198. return result;
  199. }
  200.  
  201. /**
  202. * 获取ftp目录下的所有文件
  203. * @param dir
  204. * @return
  205. */
  206. public FTPFile[] getFiles(String dir) throws Exception {
  207. getFTPClient();
  208. FTPFile[] files = new FTPFile[0];
  209. try {
  210. files = ftpClient.listFiles(dir);
  211. }catch (Throwable thr){
  212. thr.printStackTrace();
  213. }
  214. return files;
  215. }
  216.  
  217. /**
  218. * 获取ftp目录下的某种类型的文件
  219. * @param dir
  220. * @param filter
  221. * @return
  222. */
  223. public FTPFile[] getFiles(String dir, FTPFileFilter filter) throws Exception {
  224. getFTPClient();
  225. FTPFile[] files = new FTPFile[0];
  226. try {
  227. files = ftpClient.listFiles(dir, filter);
  228. }catch (Throwable thr){
  229. thr.printStackTrace();
  230. }
  231. return files;
  232. }
  233.  
  234. /**
  235. * 创建文件夹
  236. * @param remoteDir
  237. * @return 如果已经有这个文件夹返回false
  238. */
  239. public boolean makeDirectory(String remoteDir) throws Exception {
  240. getFTPClient();
  241. boolean result = false;
  242. try {
  243. result = ftpClient.makeDirectory(remoteDir);
  244. } catch (IOException e) {
  245. e.printStackTrace();
  246. }
  247. return result;
  248. }
  249.  
  250. public boolean mkdirs(String dir) throws Exception {
  251. boolean result = false;
  252. if (null == dir) {
  253. return result;
  254. }
  255. getFTPClient();
  256. ftpClient.changeWorkingDirectory("/");
  257. StringTokenizer dirs = new StringTokenizer(dir, "/");
  258. String temp = null;
  259. while (dirs.hasMoreElements()) {
  260. temp = dirs.nextElement().toString();
  261. //创建目录
  262. ftpClient.makeDirectory(temp);
  263. //进入目录
  264. ftpClient.changeWorkingDirectory(temp);
  265. result = true;
  266. }
  267. ftpClient.changeWorkingDirectory("/");
  268. return result;
  269. }
  270. }
  271.  

创建FtpClientTest.java测试类

上传一张图片到FTP服务器,并将文件重新命名为hrabbit.jpg,代码如下:

  1. /**
  2. * FtpClient测试
  3. * @Auther: hrabbit
  4. * @Date: 2018-12-21 9:14 PM
  5. * @Description:
  6. */
  7. @RunWith(SpringRunner.class)
  8. @SpringBootTest
  9. public class FtpClientTest {
  10.  
  11. /**
  12. * 测试上传
  13. */
  14. @Test
  15. public void uploadFile(){
  16. boolean flag = FTPUtils.uploadFile("hrabbit.jpg", "/Users/mrotaku/Downloads/klklklkl_4x.jpg");
  17. Assert.assertEquals(true, flag);
  18. }
  19.  
  20. }

程序完美运行,这时候我们查看我们的FTP服务器,http://localhost:8866/hrabbit.jpg

码云地址:https://gitee.com/hrabbit/hrabbit-admin

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号