接著上一篇進(jìn)行學(xué)習(xí)java文件上傳下載1。
五、斷點(diǎn)續(xù)傳
對于熟用QQ的程序員,QQ的斷點(diǎn)續(xù)傳功能應(yīng)該是印象很深刻的。因?yàn)樗軐?shí)用也很方面。因此,在我們的上傳下載過程中,很實(shí)現(xiàn)了斷點(diǎn)續(xù)傳的功能。
其實(shí)斷點(diǎn)續(xù)傳的原理很簡單,就在上傳的過程中,先去服務(wù)上進(jìn)行查找,是否存在此文件,如果存在些文件,則比較服務(wù)器上文件的大小與本地文件的大小,如果服務(wù)器上的文件比本地的要小,則認(rèn)為此文件上傳過程中應(yīng)該可以進(jìn)行斷點(diǎn)續(xù)傳。
在實(shí)現(xiàn)的過程中,RandomAccessFile類變得很有用。此類的實(shí)例支持對隨機(jī)存取文件的讀取和寫入。隨機(jī)存取文件的行為類似存儲在文件系統(tǒng)中的一個(gè)大型字節(jié)數(shù)組。存在指向該隱含數(shù)組的光標(biāo)或索引,稱為文件指針;輸入操作從文件指針開始讀取字節(jié),并隨著對字節(jié)的讀取而前移此文件指針。如果隨機(jī)存取文件以讀取/寫入模式創(chuàng)建,則輸出操作也可用;輸出操作從文件指針開始寫入字節(jié),并隨著對字節(jié)的寫入而前移此文件指針。寫入隱含數(shù)組的當(dāng)前末尾之后的輸出操作導(dǎo)致該數(shù)組擴(kuò)展。該文件指針可以通過 getFilePointer 方法讀取,并通過 seek 方法進(jìn)行設(shè)置。
RandomAccessFile類的skipBytes方法嘗試跳過輸入的 n 個(gè)字節(jié)以丟棄跳過的字節(jié)。如果從服務(wù)器上查得待上傳文件的大小n,則采用skipBytes方法可以跳過這n個(gè)字節(jié),從而開始從新的地方開始進(jìn)行斷點(diǎn)續(xù)傳。具體的方法說明可以參見JDK5的API說明。
可以在net.sf.jftp.net. DataConnection類的run方法中,可以看出上傳下載中斷點(diǎn)續(xù)傳的實(shí)現(xiàn),代碼如下:
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
public void run() { try { newLine = con.getCRLF(); if (Settings.getFtpPasvMode()) { try { sock = new Socket(host, port); sock.setSoTimeout(Settings.getSocketTimeout()); } catch (Exception ex) { ok = false ; debug( "Can't open Socket on port " + port); } } else { //Log.debug("trying new server socket: "+port); try { ssock = new ServerSocket(port); } catch (Exception ex) { ok = false ; Log.debug( "Can't open ServerSocket on port " + port); } } } catch (Exception ex) { debug(ex.toString()); } isThere = true ; boolean ok = true ; RandomAccessFile fOut = null ; BufferedOutputStream bOut = null ; RandomAccessFile fIn = null ; try { if (!Settings.getFtpPasvMode()) { int retry = 0 ; while ((retry++ < 5 ) && (sock == null )) { try { ssock.setSoTimeout(Settings.connectionTimeout); sock = ssock.accept(); } catch (IOException e) { sock = null ; debug( "Got IOException while trying to open a socket!" ); if (retry == 5 ) { debug( "Connection failed, tried 5 times - maybe try a higher timeout in Settings.java" ); } finished = true ; throw e; } finally { ssock.close(); } debug( "Attempt timed out, retrying" ); } } if (ok) { byte [] buf = new byte [Settings.bufferSize]; start = System.currentTimeMillis(); int buflen = 0 ; //---------------download,下載---------------------- if (type.equals(GET) || type.equals(GETDIR)) { if (!justStream) { try { if (resume) { File f = new File(file); fOut = new RandomAccessFile(file, "rw" ); fOut.skipBytes(( int ) f.length()); buflen = ( int ) f.length(); } else { if (localfile == null ) { localfile = file; } File f2 = new File(Settings.appHomeDir); f2.mkdirs(); File f = new File(localfile); if (f.exists()) { f.delete(); } bOut = new BufferedOutputStream( new FileOutputStream(localfile), Settings.bufferSize); } } catch (Exception ex) { debug( "Can't create outputfile: " + file); ok = false ; ex.printStackTrace(); } } //---------------upload,上傳---------------------- if (type.equals(PUT) || type.equals(PUTDIR)) { if (in == null ) { try { fIn = new RandomAccessFile(file, "r" ); if (resume) { fIn.skipBytes(skiplen); } //fIn = new BufferedInputStream(new FileInputStream(file)); } catch (Exception ex) { debug( "Can't open inputfile: " + " (" + ex + ")" ); ok = false ; } } if (ok) { try { out = new BufferedOutputStream(sock.getOutputStream()); } catch (Exception ex) { ok = false ; debug( "Can't get OutputStream" ); } if (ok) { try { int len = skiplen; char b; while ( true ) { int read; if (in != null ) { read = in.read(buf); } else { read = fIn.read(buf); } len += read; //System.out.println(file + " " + type+ " " + len + " " + read); if (read == - 1 ) { break ; } if (newLine != null ) { byte [] buf2 = modifyPut(buf, read); out.write(buf2, 0 , buf2.length); } else { out.write(buf, 0 , read); } con.fireProgressUpdate(file, type, len); if (time()) { // Log.debugSize(len, false, false, file); } if (read == StreamTokenizer.TT_EOF) { break ; } } out.flush(); //Log.debugSize(len, false, true, file); } catch (IOException ex) { ok = false ; debug( "Error: Data connection closed." ); con.fireProgressUpdate(file, FAILED, - 1 ); ex.printStackTrace(); } } } } } } catch (IOException ex) { Log.debug( "Can't connect socket to ServerSocket" ); ex.printStackTrace(); } finally { try { if (out != null ) { out.flush(); out.close(); } } catch (Exception ex) { ex.printStackTrace(); } try { if (bOut != null ) { bOut.flush(); bOut.close(); } } catch (Exception ex) { ex.printStackTrace(); } try { if (fOut != null ) { fOut.close(); } } catch (Exception ex) { ex.printStackTrace(); } try { if (in != null && !justStream) { in.close(); } if (fIn != null ) { fIn.close(); } } catch (Exception ex) { ex.printStackTrace(); } } try { sock.close(); } catch (Exception ex) { debug(ex.toString()); } if (!Settings.getFtpPasvMode()) { try { ssock.close(); } catch (Exception ex) { debug(ex.toString()); } } finished = true ; if (ok) { con.fireProgressUpdate(file, FINISHED, - 1 ); } else { con.fireProgressUpdate(file, FAILED, - 1 ); } } |
六、FTP端口映射
FTP的數(shù)據(jù)連接有PASV和PORT兩種,如果你的FTP服務(wù)器位于內(nèi)網(wǎng)中,需要做端口映射。筆者剛開始時(shí)對FTP的網(wǎng)外網(wǎng)映射也是不怎么了解,因此開始走了不少的彎路,開始一直以為是自己的程序有問題,浪費(fèi)了不少時(shí)間,希望通過這段,能讓大家在開發(fā)的時(shí)候少花或不花這些無謂的時(shí)間與精力。
PCD上曾經(jīng)有一篇文章介紹過一種直接訪問內(nèi)網(wǎng)的方法,其實(shí)我們只要用端口映射工具,就可輕松實(shí)現(xiàn)穿透內(nèi)網(wǎng)的目的。“端口映射器”就是一款這樣的工具,更值得一提的是,它擺脫了命令行模式,提供了圖形界面的操作環(huán)境。
為了讓各位能更加明白,先說一下原理。假設(shè)現(xiàn)在有一個(gè)局域網(wǎng),主機(jī)為A,局域網(wǎng)內(nèi)除了主機(jī)外,還有一臺機(jī)器為B,B機(jī)器當(dāng)然是通過主機(jī)A上網(wǎng)的。另外還有一臺可上網(wǎng)的機(jī)器為C,與A和B并不在一個(gè)局域網(wǎng)內(nèi)。通常情況下,C機(jī)器只能訪問到A主機(jī),而無法穿透局域網(wǎng),訪問到B。而通過端口映射后,當(dāng)C機(jī)器訪問主機(jī)A的指定端口時(shí),主機(jī)A上的“端口映射器”就起作用了,它會把指定端口上的數(shù)據(jù)轉(zhuǎn)到局域網(wǎng)內(nèi)另一臺機(jī)器的指定端口上,從而實(shí)現(xiàn)訪問內(nèi)網(wǎng)機(jī)器的目的。這樣說,大家明白了吧。至于具體的如何進(jìn)行配置,筆者認(rèn)為應(yīng)該不是件很難的事情,再說,網(wǎng)上這樣的圖形解釋很多,請大家參考網(wǎng)絡(luò)上的文章進(jìn)行設(shè)置。
當(dāng)然,實(shí)現(xiàn)直接訪問內(nèi)網(wǎng)的優(yōu)點(diǎn)是顯而易見的,別的不說,起碼FTP資源是被充分利用了。不過必須提醒讀者的是,直接訪問內(nèi)網(wǎng)可能使內(nèi)網(wǎng)的安全性受到威脅。筆者相信大部分朋友對主機(jī)安全的重要性還是重視的,但往往會忽略內(nèi)網(wǎng)機(jī)器的安全設(shè)置。一旦你實(shí)現(xiàn)了直接訪問內(nèi)網(wǎng),那就必須像對待主機(jī)一樣對待內(nèi)網(wǎng)機(jī)器,否則你的整個(gè)網(wǎng)絡(luò)將可能處于危險(xiǎn)狀態(tài)。
訪問客戶端資源
Java應(yīng)用程序環(huán)境的安全策略,對于不同的代碼所擁有的不同資源的許可,它由一個(gè)Policy對象來表達(dá)。為了讓Applet(或者運(yùn)行在 SecurityManager下的一個(gè)應(yīng)用程序)能夠執(zhí)行受保護(hù)的行為,例如讀寫文件,Applet(或 Java應(yīng)用程序)必須獲得那項(xiàng)操作的許可,安全策略文件就是用來實(shí)現(xiàn)這些許可。
Policy對象可能有多個(gè)實(shí)體,雖然任何時(shí)候只能有一個(gè)起作用。當(dāng)前安裝的Policy對象,在程序中可以通過調(diào)用getPolicy方法得到,也可以通過調(diào)用setPolicy方法改變。Policy對象評估整個(gè)策略,返回一個(gè)適當(dāng)?shù)腜ermissions對象,詳細(xì)說明哪些代碼可以訪問哪些資源。策略文件可以儲存在無格式的ASCII文件或Policy類的二進(jìn)制文件或數(shù)據(jù)庫中。本文僅討論無格式的ASCII文件的形式。
在實(shí)際使用中,我們可以不需要自己手動去編寫那么復(fù)雜的java.policy文件,特別是在不使用數(shù)字簽名時(shí)。這時(shí),我們完全可以借鑒JRE提供給我們的現(xiàn)成的 C:\Program Files\Java\jre1.5.0_12\lib\security\java.policy文件,根據(jù)我們的需要做相應(yīng)的修改,本文就針對不使用數(shù)字簽名情況編寫安全策略文件。下面,是一個(gè)完整的在Windows NT/XP下使用的java.policy文件。在文件中,分別使用注釋的形式說明了每個(gè)“permission”記錄的用途。當(dāng)然,不同的程序?qū)Y源訪問權(quán)限的要求可能不一樣,可以根據(jù)項(xiàng)目需要進(jìn)行調(diào)整與選擇。
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
|
grant { //對系統(tǒng)和用戶目錄“讀”的權(quán)限 permission java.util.PropertyPermission "user.dir" , "read" ; permission java.util.PropertyPermission "user.home" , "read" ; permission java.util.PropertyPermission "java.home" , "read" ; permission java.util.PropertyPermission "java.class.pat" , "read" ; permission java.util.PropertyPermission "user.name" , "read" ; //對線程和線程組的操作權(quán)限 permission java.lang.RuntimePermission "accessClassInPackage.sun.misc" ; permission java.lang.RuntimePermission "accessClassInPackage.sun.audio" ; permission java.lang.RuntimePermission "modifyThread" ; permission java.lang.RuntimePermission "modifyThreadGroup" ; permission java.lang.RuntimePermission "loadLibrary.*" ; //讀寫文件的權(quán)限 permission java.io.FilePermission "<<ALL FILES>>" , "read" ; permission java.io.FilePermission "${user.dir}${/}jmf.log" , "write" ; permission java.io.FilePermission "${user.home}${/}.JMStudioCfg" , "write" ; permission java.net.SocketPermissio "*" , "connect,accept" ; permission java.io.FilePermission "C:\WINNT\TEMP\*" , "write" ; permission java.io.FilePermission "C:\WINNT\TEMP\*" , "delete" ; permission java.awt.AWTPermission "showWindowWithoutWarningBanner" ; permission javax.sound.sampled.AudioPermission "record" ; // //操作Socket端口的各種權(quán)限 permission java.net.SocketPermission "-" , "listen" ; permission java.net.SocketPermission "-" , "accept" ; permission java.net.SocketPermission "-" , "connect" ; permission java.net.SocketPermission "-" , "resolve" ; permission java.security.AllPermission; }; grant signedBy "saili" { permission java.net.SocketPermission "*:1024-65535" , "connect,accept,resolve" ; permission java.net.SocketPermission "*:80" , "connect" ; permission java.net.SocketPermission "-" , "listen, accept, connect, listen, resolve" , signedBy "ganja" ; permission java.net.SocketPermission "-" , "accept" ; permission java.net.SocketPermission "-" , "connect" ; permission java.net.SocketPermission "-" , "resolve" ; permission java.security.AllPermission; }; |
筆者在本項(xiàng)目中,為了使用客戶端的用戶設(shè)置更加的方便與簡單,將上面的文件采用VB或C#來做成一個(gè)小程序來寫。然后將JRE及些exe共同打成一個(gè)EXE包,當(dāng)JRE安裝完成后,此小程序負(fù)責(zé)找到JRE在操作系統(tǒng)中的安裝路徑,并在程序中寫出此java.policy文件,覆蓋原有的文件。如此一來,用戶就只需安裝一個(gè)EXE文件,從而簡化了安裝的操作次數(shù)。
七、Applet回調(diào)服務(wù)器
JavaScript與Applet之間能夠相互通訊給我們帶來了很多方便,Java與JavaScript互相補(bǔ)充,以開發(fā)功能更完美的Web應(yīng)用程序。B/S下能夠充分利用java的優(yōu)勢,給我們帶來更多的網(wǎng)絡(luò)體驗(yàn),方便用戶。筆者用的比較多的是利用Swing組件開發(fā)的應(yīng)用程序利用Applet實(shí)現(xiàn)B/s下架構(gòu),這樣能夠充分顯示Swing組件的優(yōu)勢,便于系統(tǒng)升級,便于維護(hù);還有就是在WEB下,有時(shí)客戶端要使用本地的硬件資源,筆者所知道的是通過Applet來實(shí)現(xiàn),通過Applet去調(diào)用javaAPI來實(shí)現(xiàn)。 我們具體來看看JavaScript與Applet之間到底是怎樣通訊的呢?
1.JavaScript訪問Applet
<applet name="appletName" ....../>//JavaScript訪問Applet屬性。
window.document.appletName.appletField (屬性必須是public的,"window.document."也可以不寫) //JavaScript訪問Applet方法。
window.document.appletName.appletMethod (方法必須是public的,"window.document."也可以不寫)。
2.Applet訪問JavaScript
Live Connect提供了Java與JavaScript的接口,可以允許在Java Applet小程序中使用JavaScript。
需要用到一個(gè)jar包,在C:\Program Files\Java\目錄下找,大概有5M多,其實(shí)就是打開看哪個(gè)有netscape.javascript.JSObject。如果沒有裝個(gè)NetScape或從網(wǎng)上下都可以。 可以把它重命名為netscape.jar(不是必須的),一定要加入到classpath,目的是使開發(fā)的時(shí)候能夠編譯。特別注意的是:部署時(shí)不需要包括netscape.jar,因?yàn)檎麄€(gè)包會下載到客戶端,影響速度。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//引入netscape類 import netscape.javascript.JSObject; import netscape.javascript.JSException; //可允許在小程序中處理異常事件 public void callJavaScript(String callBackJavascript) { JSObject window = JSObject.getWindow( this ); // 獲取JavaScript窗口句柄,引用當(dāng)前文檔窗口 JSObject docment = (JSObject) window.getMember( "document" ); form=(JSObject)doc.getMember( "textForm" ); //訪問JavaScript form對象 textField=(JSObject)form.getMember( "textField" );訪問JavaScript text對象 text=(String) textField.getMember( "value" ); //獲取文本區(qū)的值 // 調(diào)用JavaScript的alert()方法 // window.eval("alert(\"This alert comes from Java!\")"); window.call(callBackJavascript, null ); // 參數(shù)用數(shù)組的形式表示。 } |
八、運(yùn)行效果
1.上傳
(1).啟動上傳上面
(2).上傳中
(3).上傳中
(4).上傳成功
2.下載
(1)下載文件的保存路徑
(2)下載中
(3)下載中
(4)下載成功
九、小結(jié)
在本文中,筆者將在實(shí)際項(xiàng)目中的上傳下載問題的解決方案進(jìn)行了闡述,通過采用FTP協(xié)議,來達(dá)到批量的,基本W(wǎng)eb的大文件的上傳下載。同時(shí)通過Applet技術(shù)實(shí)現(xiàn)在客戶端對本地資源進(jìn)行訪問。就一些大家經(jīng)常遇到的實(shí)際功能如進(jìn)度條、斷點(diǎn)續(xù)傳、FTP內(nèi)外網(wǎng)映射等問題進(jìn)行了初步的探討。這是筆者基于一些FTP的Java客戶端庫的基礎(chǔ)應(yīng)用。希望對讀者有一定的借鑒作用。對其中一些未盡事宜進(jìn)行補(bǔ)充。還有一些比較容易而且網(wǎng)上都有說明或?qū)嵗膬?nèi)容在此沒有一一列舉。如FTP在服務(wù)器端Serv-U軟件如何建立FTP服務(wù)、Applet在JSP頁面中的嵌入方式及參數(shù)傳遞方法、在Eclipse或是NetBeans下開始Applet等內(nèi)容,由于篇幅的限制,并沒有詳盡描述,請讀者參考網(wǎng)上的例子或其他參考資料。
下載地址:FTPTransfer.rar
注釋,考慮到版權(quán)的問題,沒有把JAVA類文件發(fā)上來,不過這樣的JAR文件如何還原成java文件,我想大家已經(jīng)是很熟悉了吧,呵呵.
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。