Posted on 2007-10-22 09:43
詩(shī)特林 閱讀(11575)
評(píng)論(15) 編輯 收藏 所屬分類:
Socket
用Java實(shí)現(xiàn)FTP批量大文件上傳下載(二)
用Java實(shí)現(xiàn)FTP批量大文件上傳下載(一)
2 上傳下載
文件的上傳可以分成多線程及單線程,在單線程情況下比較簡(jiǎn)單,而在多線程的情況下,要處理的事情要多點(diǎn),同時(shí)也要小心很多。下面是net.sf.jftp.net.FtpConnection的上傳handleUpload方法。已經(jīng)考慮了單線程及多線程兩種不同的類型。
public int handleUpload(String file, String realName)

{
if(Settings.getEnableMultiThreading() &&
(!Settings.getNoUploadMultiThreading()))

{
Log.out("spawning new thread for this upload.");

FtpTransfer t;

if(realName != null)

{
t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(),
file, username, password, Transfer.UPLOAD,
handler, listeners, realName, crlf);
}
else

{
t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(),
file, username, password, Transfer.UPLOAD,
handler, listeners, crlf);
}

lastTransfer = t;

return NEW_TRANSFER_SPAWNED;
}
else

{
if(Settings.getNoUploadMultiThreading())

{
Log.out("upload multithreading is disabled.");
}
else

{
Log.out("multithreading is completely disabled.");
}

return (realName == null) ? upload(file) : upload(file, realName);
}
}

在多線程的情況下,有一個(gè)單獨(dú)的類net.sf.jftp.net .FtpTransfer,當(dāng)然,多線程情況下,此類肯定是一個(gè)單獨(dú)的線程了。與JConnection相似,其線程的啟動(dòng)也是在構(gòu)造方法中啟動(dòng)。而在它的run方法中,進(jìn)行文件的讀取及傳輸。
public void run()

{
if(handler.getConnections().get(file) == null)

{
handler.addConnection(file, this);
}
else if(!pause)

{
Log.debug("Transfer already in progress: " + file);
work = false;
stat = 2;

return;
}

boolean hasPaused = false;

while(pause)

{
try

{
runner.sleep(100);

if(listeners != null)

{
for(int i = 0; i < listeners.size(); i++)

{
((ConnectionListener) listeners.elementAt(i)).updateProgress(file,
PAUSED,
-1);
}
}

if(!work)

{
if(listeners != null)

{
for(int i = 0; i < listeners.size(); i++)

{
((ConnectionListener) listeners.elementAt(i)).updateProgress(file,
REMOVED,
-1);
}
}
}
}
catch(Exception ex)

{
}

hasPaused = true;
}

while((handler.getConnectionSize() >= Settings.getMaxConnections()) &&
(handler.getConnectionSize() > 0) && work)

{
try

{
stat = 4;
runner.sleep(400);

if(!hasPaused && (listeners != null))

{
for(int i = 0; i < listeners.size(); i++)

{
((ConnectionListener) listeners.elementAt(i)).updateProgress(file,
QUEUED,
-1);
}
}
else

{
break;
}
}
catch(Exception ex)

{
ex.printStackTrace();
}
}

if(!work)

{
if(listeners != null)

{
for(int i = 0; i < listeners.size(); i++)

{
((ConnectionListener) listeners.elementAt(i)).updateProgress(file,
REMOVED,
-1);
}
}

handler.removeConnection(file);
stat = 3;

return;
}

started = true;

try

{
runner.sleep(Settings.ftpTransferThreadPause);
}
catch(Exception ex)

{
}

con = new FtpConnection(host, port, remotePath, crlf);

con.setConnectionHandler(handler);
con.setConnectionListeners(listeners);

int status = con.login(user, pass);

if(status == FtpConnection.LOGIN_OK)

{
File f = new File(localPath);
con.setLocalPath(f.getAbsolutePath());

if(type.equals(UPLOAD))

{
if(newName != null)

{
transferStatus = con.upload(file, newName);
}
else

{
transferStatus = con.upload(file);
}
}
else

{
transferStatus = con.download(file,this.newName);
}
}

if(!pause)

{
handler.removeConnection(file);
}
}


至于下載的過(guò)程,因?yàn)樗巧蟼鞯哪孢^(guò)程,與上傳的方法及寫(xiě)法大同小異,在些出于篇幅的考慮,并沒(méi)有將代碼列出,但其思想及思路完全一樣。請(qǐng)讀者參考源代碼。
四、 進(jìn)度條
可以想象,如果在上傳或是下載的過(guò)程中,沒(méi)有任何的提示,用戶根本沒(méi)法判斷任務(wù)是否完成或是任務(wù)是否死了,常常由于上傳時(shí)間或下載時(shí)間過(guò)長(zhǎng)而誤導(dǎo)用戶。因此,進(jìn)度條就顯得非常的重要與實(shí)用。
進(jìn)度條的實(shí)現(xiàn),其實(shí)說(shuō)起來(lái)很簡(jiǎn)單。就是在程序中開(kāi)啟兩個(gè)線程,第一個(gè)線程用于動(dòng)態(tài)的改變界面上進(jìn)度條的value值,而第二個(gè)線程則在上傳或是下載的過(guò)程中,做成一個(gè)循環(huán),在此循環(huán)中,每次讀取一定數(shù)量如8192字節(jié)數(shù)的數(shù)據(jù)。然后傳完此數(shù)據(jù)后,調(diào)用第一個(gè)線程中的updateProgress方法,來(lái)更新界面進(jìn)度條的value值。
而上傳或下載的過(guò)程中(見(jiàn)上一節(jié)的FtpTransfer類的run方法),可以查看,con.upload(file, newName)方法,代碼如下所示,
public int upload(String file, String realName, InputStream in)

{
hasUploaded = true;
Log.out("ftp upload started: " + this);

int stat;

if((in == null) && new File(file).isDirectory())

{
shortProgress = true;
fileCount = 0;
baseFile = file;
dataType = DataConnection.PUTDIR;
isDirUpload = true;

stat = uploadDir(file);

shortProgress = false;

//System.out.println(fileCount + ":" + baseFile);
fireProgressUpdate(baseFile,
DataConnection.DFINISHED + ":" + fileCount, -1);

fireActionFinished(this);
fireDirectoryUpdate(this);
}
else

{
dataType = DataConnection.PUT;
stat = rawUpload(file, realName, in);

try

{
Thread.sleep(100);
}
catch(Exception ex)

{
}

fireActionFinished(this);
fireDirectoryUpdate(this);
}

try

{
Thread.sleep(500);
}
catch(Exception ex)

{
}

return stat;
}

此方法進(jìn)行負(fù)責(zé)上傳一定字節(jié)數(shù)量的內(nèi)容,其實(shí)就是調(diào)用rawUpload方法,這里沒(méi)列出,請(qǐng)參考源代碼,而當(dāng)傳完此字節(jié)數(shù)據(jù)后,通過(guò)調(diào)用fireActionFinished()方法來(lái)調(diào)用主線程中的updateProgressBar()方法。其實(shí)代碼如下:

protected void updateProgressBar()
{
int percent = (int) (((float) lFileCompleteSize / (float) lFileSize) * 10000F);
pbFile.setValue(percent);
// System.out.println("================================================="+percent);
pbFile.setString(lFileCompleteSize / 1024L + "/" + lFileSize / 1024L
+ " kB");
percent = (int) (((float) lTotalCompleteSize / (float) lTotalSize) * 10000F);
pbTotal.setString(lTotalCompleteSize / 1024L + "/" + lTotalSize / 1024L
+ " kB");
pbTotal.setValue(percent);
repaint();
}

上面用了兩個(gè)進(jìn)度條,第一個(gè)進(jìn)度條表示當(dāng)前文件的上傳或下載進(jìn)度,第二個(gè)進(jìn)度條表示所有文件下載或上傳的進(jìn)度。同時(shí),為了產(chǎn)生進(jìn)度條的移動(dòng)或變化進(jìn)度幅度比較明顯,通過(guò)pbFile.setMaximum(10000)及pbTotal.setMaximum(10000)將進(jìn)度條的最大值設(shè)置成10000,而不是平時(shí)我們所設(shè)置的100。筆者認(rèn)為這樣比較好看,因?yàn)橛械臅r(shí)候上傳或下載的時(shí)候由于網(wǎng)絡(luò)原因,可能變化比較小。若設(shè)置成100則變化不是特別明顯。