最近在寫一個ftp上傳工具,用到了apache的ftpclient,但是每個線程頻繁的創建和銷毀ftpclient對象對服務器的壓力很大,因此,此處最好使用一個ftpclient連接池。仔細翻了一下apache的api,發現它并沒有一個ftpclientpool的實現,所以,不得不自己寫一個ftpclientpool。下面就大體介紹一下開發連接池的整個過程,供大家參考。
我們可以利用apache提供的common-pool包來協助我們開發連接池。而開發一個簡單的對象池,僅需要實現common-pool 包中的objectpool和poolableobjectfactory兩個接口即可。
線程池的意義
為了減少頻繁創建、銷毀對象帶來的性能消耗,我們可以利用對象池的技術來實現對象的復用。對象池提供了一種機制,它可以管理對象池中對象的生命周期,提供了獲取和釋放對象的方法,可以讓客戶端很方便的使用對象池中的對象。
pom引入依賴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!-- ftpclient依賴包--> <dependency> <groupid>commons-net</groupid> <artifactid>commons-net</artifactid> <version> 3.5 </version> </dependency> <!-- 線程池--> <dependency> <groupid>commons-pool</groupid> <artifactid>commons-pool</artifactid> <version> 1.6 </version> </dependency> <dependency> <groupid>org.apache.commons</groupid> <artifactid>commons-pool2</artifactid> <version> 2.0 </version> </dependency> |
創建ftp配置信息
在resources目錄下創建ftp.properties配置文件,目錄結構如下:
添加如下的配置信息:
1
2
3
4
5
6
7
8
9
10
|
########### ftp用戶名稱 ########### ftp.username=hrabbit ########### ftp用戶密碼 ########### ftp.password= 123456 ########### ftp主機ip ########### ftp.host= 127.0 . 0.1 ########### ftp主機端口號 ########### ftp.port= 21 ########### 保存根路徑 ########### ftp.baseurl=/ |
創建ftpproperties.java配置文件
加載配置內容到spring中,配置信息基本延用我的就可以。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
/** * ftp的配置信息 * @auther: hrabbit * @date: 2018-12-03 2:06 pm * @description: */ @data @component @propertysource ( "classpath:ftp.properties" ) @configurationproperties (prefix = "ftp" ) public class ftpproperties { private string username; private string password; private string host; private integer port; private string baseurl; private integer passivemode = ftp.binary_file_type; private string encoding= "utf-8" ; private int clienttimeout= 120000 ; private int buffersize; private int transferfiletype=ftp.binary_file_type; private boolean renameuploaded; private int retrytime; } |
創建ftpclientpool線程池
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/** * 自定義實現ftp連接池 * @auther: hrabbit * @date: 2018-12-03 3:40 pm * @description: */ @slf4j @suppresswarnings ( "all" ) public class ftpclientpool implements objectpool<ftpclient> { private static final int default_pool_size = 10 ; public blockingqueue<ftpclient> blockingqueue; private ftpclientfactory factory; public ftpclientpool(ftpclientfactory factory) throws exception { this (default_pool_size, factory); } public ftpclientpool( int poolsize, ftpclientfactory factory) throws exception { this .factory = factory; this .blockingqueue = new arrayblockingqueue<ftpclient>(poolsize); initpool(poolsize); } /** * 初始化連接池 * @param maxpoolsize * 最大連接數 * @throws exception */ private void initpool( int maxpoolsize) throws exception { int count = 0 ; while (count < maxpoolsize) { this .addobject(); count++; } } /** * 從連接池中獲取對象 */ @override public ftpclient borrowobject() throws exception { ftpclient client = blockingqueue.take(); if (client == null ) { client = factory.makeobject(); } else if (!factory.validateobject(client)) { invalidateobject(client); client = factory.makeobject(); } return client; } /** * 返還一個對象(鏈接) */ @override public void returnobject(ftpclient client) throws exception { if ((client != null ) && !blockingqueue.offer(client, 2 ,timeunit.minutes)) { try { factory.destroyobject(client); } catch (exception e) { throw e; } } } /** * 移除無效的對象(ftp客戶端) */ @override public void invalidateobject(ftpclient client) throws exception { blockingqueue.remove(client); } /** * 增加一個新的鏈接,超時失效 */ @override public void addobject() throws exception { blockingqueue.offer(factory.makeobject(), 2 , timeunit.minutes); } /** * 重新連接 */ public ftpclient reconnect() throws exception { return factory.makeobject(); } /** * 獲取空閑鏈接數(這里暫不實現) */ @override public int getnumidle() { return blockingqueue.size(); } /** * 獲取正在被使用的鏈接數 */ @override public int getnumactive() { return default_pool_size - getnumidle(); } @override public void clear() throws exception { } /** * 關閉連接池 */ @override public void close() { try { while (blockingqueue.iterator().hasnext()) { ftpclient client = blockingqueue.take(); factory.destroyobject(client); } } catch (exception e) { log.error( "close ftp client pool failed...{}" , e); } } /** * 增加一個新的鏈接,超時失效 */ public void addobject(ftpclient ftpclient) throws exception { blockingqueue.put(ftpclient); } } |
創建一個ftpclientfactory工廠類
創建ftpclientfactory實現poolableobjectfactory的接口,ftpclient工廠類,通過ftpclient工廠提供ftpclient實例的創建和銷毀
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
/** * ftpclient 工廠 * @auther: hrabbit * @date: 2018-12-03 3:41 pm * @description: */ @slf4j @suppresswarnings ( "all" ) public class ftpclientfactory implements poolableobjectfactory<ftpclient> { private ftpproperties ftpproperties; public ftpclientfactory(ftpproperties ftpproperties) { this .ftpproperties = ftpproperties; } @override public ftpclient makeobject() throws exception { ftpclient ftpclient = new ftpclient(); ftpclient.setcontrolencoding(ftpproperties.getencoding()); ftpclient.setconnecttimeout(ftpproperties.getclienttimeout()); try { ftpclient.connect(ftpproperties.gethost(), ftpproperties.getport()); int reply = ftpclient.getreplycode(); if (!ftpreply.ispositivecompletion(reply)) { ftpclient.disconnect(); log.warn( "ftpserver refused connection" ); return null ; } boolean result = ftpclient.login(ftpproperties.getusername(), ftpproperties.getpassword()); ftpclient.setfiletype(ftpproperties.gettransferfiletype()); if (!result) { log.warn( "ftpclient login failed... username is {}" , ftpproperties.getusername()); } } catch (exception e) { log.error( "create ftp connection failed...{}" , e); throw e; } return ftpclient; } @override public void destroyobject(ftpclient ftpclient) throws exception { try { if (ftpclient != null && ftpclient.isconnected()) { ftpclient.logout(); } } catch (exception e) { log.error( "ftp client logout failed...{}" , e); throw e; } finally { if (ftpclient != null ) { ftpclient.disconnect(); } } } @override public boolean validateobject(ftpclient ftpclient) { try { return ftpclient.sendnoop(); } catch (exception e) { log.error( "failed to validate client: {}" ); } return false ; } @override public void activateobject(ftpclient obj) throws exception { //do nothing } @override public void passivateobject(ftpclient obj) throws exception { //do nothing } } |
創建ftputils.java的工具類
ftputils.java中封裝了上傳、下載等方法,在項目啟動的時候,在@postconstruct注解的作用下通過執行init()的方法,創建ftpclientfactory工廠中,并初始化了ftpclientpool線程池,這樣每次調用方法的時候,都直接從ftpclientpool中取出一個ftpclient對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
/** * @auther: hrabbit * @date: 2018-12-03 3:47 pm * @description: */ @slf4j @component public class ftputils { /** * ftp的連接池 */ @autowired public static ftpclientpool ftpclientpool; /** * ftpclient對象 */ public static ftpclient ftpclient; private static ftputils ftputils; @autowired private ftpproperties ftpproperties; /** * 初始化設置 * @return */ @postconstruct public boolean init() { ftpclientfactory factory = new ftpclientfactory(ftpproperties); ftputils = this ; try { ftpclientpool = new ftpclientpool(factory); } catch (exception e) { e.printstacktrace(); return false ; } return true ; } /** * 獲取連接對象 * @return * @throws exception */ public static ftpclient getftpclient() throws exception { //初始化的時候從隊列中取出一個連接 if (ftpclient== null ) { synchronized (ftpclientpool) { ftpclient = ftpclientpool.borrowobject(); } } return ftpclient; } /** * 當前命令執行完成命令完成 * @throws ioexception */ public void complete() throws ioexception { ftpclient.completependingcommand(); } /** * 當前線程任務處理完成,加入到隊列的最后 * @return */ public void disconnect() throws exception { ftpclientpool.addobject(ftpclient); } /** * description: 向ftp服務器上傳文件 * * @version1.0 * @param remotefile * 上傳到ftp服務器上的文件名 * @param input * 本地文件流 * @return 成功返回true,否則返回false */ public static boolean uploadfile(string remotefile, inputstream input) { boolean result = false ; try { getftpclient(); ftpclient.enterlocalpassivemode(); result = ftpclient.storefile(remotefile, input); input.close(); ftpclient.disconnect(); } catch (exception e) { e.printstacktrace(); } return result; } /** * description: 向ftp服務器上傳文件 * * @version1.0 * @param remotefile * 上傳到ftp服務器上的文件名 * @param localfile * 本地文件 * @return 成功返回true,否則返回false */ public static boolean uploadfile(string remotefile, string localfile){ fileinputstream input = null ; try { input = new fileinputstream( new file(localfile)); } catch (filenotfoundexception e) { e.printstacktrace(); } return uploadfile(remotefile, input); } /** * 拷貝文件 * @param fromfile * @param tofile * @return * @throws exception */ public boolean copyfile(string fromfile, string tofile) throws exception { inputstream in=getfileinputstream(fromfile); getftpclient(); boolean flag = ftpclient.storefile(tofile, in); in.close(); return flag; } /** * 獲取文件輸入流 * @param filename * @return * @throws ioexception */ public static inputstream getfileinputstream(string filename) throws exception { bytearrayoutputstream fos= new bytearrayoutputstream(); getftpclient(); ftpclient.retrievefile(filename, fos); bytearrayinputstream in= new bytearrayinputstream(fos.tobytearray()); fos.close(); return in; } /** * description: 從ftp服務器下載文件 * * @version1.0 * @return */ public static boolean downfile(string remotefile, string localfile){ boolean result = false ; try { getftpclient(); outputstream os = new fileoutputstream(localfile); ftpclient.retrievefile(remotefile, os); ftpclient.logout(); ftpclient.disconnect(); result = true ; } catch (exception e) { e.printstacktrace(); } finally { try { } catch (exception e) { e.printstacktrace(); } } return result; } /** * 從ftp中獲取文件流 * @param filepath * @return * @throws exception */ public static inputstream getinputstream(string filepath) throws exception { getftpclient(); inputstream inputstream = ftpclient.retrievefilestream(filepath); return inputstream; } /** * ftp中文件重命名 * @param fromfile * @param tofile * @return * @throws exception */ public boolean rename(string fromfile,string tofile) throws exception { getftpclient(); boolean result = ftpclient.rename(fromfile,tofile); return result; } /** * 獲取ftp目錄下的所有文件 * @param dir * @return */ public ftpfile[] getfiles(string dir) throws exception { getftpclient(); ftpfile[] files = new ftpfile[ 0 ]; try { files = ftpclient.listfiles(dir); } catch (throwable thr){ thr.printstacktrace(); } return files; } /** * 獲取ftp目錄下的某種類型的文件 * @param dir * @param filter * @return */ public ftpfile[] getfiles(string dir, ftpfilefilter filter) throws exception { getftpclient(); ftpfile[] files = new ftpfile[ 0 ]; try { files = ftpclient.listfiles(dir, filter); } catch (throwable thr){ thr.printstacktrace(); } return files; } /** * 創建文件夾 * @param remotedir * @return 如果已經有這個文件夾返回false */ public boolean makedirectory(string remotedir) throws exception { getftpclient(); boolean result = false ; try { result = ftpclient.makedirectory(remotedir); } catch (ioexception e) { e.printstacktrace(); } return result; } public boolean mkdirs(string dir) throws exception { boolean result = false ; if ( null == dir) { return result; } getftpclient(); ftpclient.changeworkingdirectory( "/" ); stringtokenizer dirs = new stringtokenizer(dir, "/" ); string temp = null ; while (dirs.hasmoreelements()) { temp = dirs.nextelement().tostring(); //創建目錄 ftpclient.makedirectory(temp); //進入目錄 ftpclient.changeworkingdirectory(temp); result = true ; } ftpclient.changeworkingdirectory( "/" ); return result; } } |
創建ftpclienttest.java測試類
上傳一張圖片到ftp服務器,并將文件重新命名為hrabbit.jpg,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/** * ftpclient測試 * @auther: hrabbit * @date: 2018-12-21 9:14 pm * @description: */ @runwith (springrunner. class ) @springboottest public class ftpclienttest { /** * 測試上傳 */ @test public void uploadfile(){ boolean flag = ftputils.uploadfile( "hrabbit.jpg" , "/users/mrotaku/downloads/klklklkl_4x.jpg" ); assert .assertequals( true , flag); } } |
程序完美運行,這時候我們查看我們的ftp服務器,http://localhost:8866/hrabbit.jpg
碼云地址:https://gitee.com/hrabbit/hrabbit-admin
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.jianshu.com/p/6270b2308c4e