摘要:本文介紹了JavaBean實作多個檔案上傳的兩種方法,分別是使用http協定和ftp協定實作。首先講述了http協定傳送多個檔案的基本格式和實作上傳的詳細過程,之後簡單介紹了使用ftpclient 類別實作了ftp方式的上傳,最後對這兩種方法進行了比較。
關鍵字:JavaBean 、http 、ftp 、ftpclient
JavaBean是一種基於Java的軟體元件。 JSP對於在Web 應用中整合JavaBean元件提供了完善的支援。這種支援不僅能縮短開發時間(可以直接利用經測試和可信任的已有元件,避免了重複開發),也為JSP應用帶來了更多的可擴展性。
文件的上傳功能在基於B/S的開發模式中非常普遍。同其他開發工具比較,JSP對檔案的上傳支援並不是很完美,它既不像ASP一定需要使用元件來完成,也不像PHP那樣直接提供了檔案上載的支援。 JSP實作檔案上傳的實作方式是這樣的:使用ServletRequest類別的getInputStream()方法取得一個客戶端向伺服器發出的資料流,然後處理這個資料流,從中分析、得到檔案上傳傳遞到伺服器的各個參數和數據,然後將其中的文件數據儲存為一個文件或插入到資料庫中。通常JSP頁面中不處理檔案的上傳功能,而是把這些功能放到Servlet 或JavaBean中去實作。使用Servlet完成檔案上傳的範例在一些JSP的相關書籍中都有所介紹,我在這裡介紹使用JeanBean是如何完成檔案上傳的。 JSP中實作檔案的上傳可以採用兩種方式即採用HTTP協定和FTP協定實現,二者在傳輸的原理上有很大的差異。以下將結合原始碼對它們的實作做簡單介紹,相信讀者會從中有所收穫。以下程序已經調試通過。除錯的環境:window 2000 server+Apache +tomcat4.0,JavaBean偵錯環境:JDK1.4+Editplus。
在JSP中使用JavaBean實作基於Web的檔案上傳功能一般需要三種檔案結合完成。這三種檔案分別是提供介面的HTML頁面檔案、完成呼叫實作上傳功能的JavaBean的JSP檔案、實作JavaBean的Java的類別檔案。以下我將重點放在採用HTTP協定和FTP協定實作檔案上傳功能的JavaBean部分。
1 採用HTTP協定實作多個檔案的上傳
在過去的Html中,表單無法實現檔案的上傳,這多少限制了一些網頁的功能。 RFC1867規範(即在Html中實作基於表單的檔案上傳)對表單作出了擴展,增加了一個表單元素〈input type=file>。透過使用這個元素,瀏覽器會自動產生一個輸入框和一個按鈕,輸入框可供使用者填寫本地的檔案名稱和路徑名,按鈕可以讓瀏覽器開啟一個檔案選擇框供使用者選擇檔案。具體的表單實作如下:
<FORMMETHOD="POST" ACTION="*.jsp" ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="FILE1" SIZE="50"><BR>
<INPUT TYPE="SUBMIT" VALUE="Upload">
</FORM>
當選擇了貼上檔案後就直接輸入本機檔案的絕對路徑,表單的action屬性值是*.jsp,這表示要求(包括上載的檔案)會傳送給*..jsp檔案。在這個過程中實際上就實作了HTTP方式的檔案上載。檔案從客戶端到伺服器的上載是由HTTP協定的通用網關介面(CGI)支援的。這種上載方式要求瀏覽器和WEBServer兩方面都能夠支援Rfc1867。 JavaBean 透過ServletRequest類別的getInputStream()方法取得一個客戶端向伺服器發出的資料流、分析上傳的檔案格式,根據分析結果將多個檔案依序輸出伺服器端的目標檔案中。本例中的JavaBeande的功能是由testUpload類別具體實作。 TestUpload類別的框架如下:
public class testUpload
{
public testUpload(){……}
public final void initialize(ServletConfig config) throws ServletException
{ m_application = config.getServletContext(); }
public void upload() throws testUploadException, IOException, ServletException
{………}
private void getDataSection(){………}
private void getDataHeader(){………}
public int save (String destPathName)
throws SmartUploadException, IOException, ServletException
{………}
……
}
透過initialize()方法初始化Servlet的運行環境。使用upload()方法取得輸入流,並分析上傳檔案的格式,並將各個上傳檔案的屬性賦給多個File類別實例處理,這些File類別實例由Files類別管理。 File類別根據各檔案的屬性呼叫它的save ()方法將多個檔案依序輸出伺服器端的目標檔案中。其中upload()方法是關鍵,用於分析http1.1協定傳送檔案的格式。經過測試,我們得出傳輸流檔案的格式,這對理解upload()方法很有用。例如,上傳我的文檔tt.txt檔案。格式如下:
//檔案分隔符
-----------------------------7d226137250336
//檔案資訊頭
Content-Disposition: form-data; name="FILE1"; filename="C:Documents and SettingsAdministrator.TIMBER-4O6B0ZZ0My Documentstt.sql"
Content-Type: text/plain
//原始檔內容
create table info(
content image null);
//下一個檔案的分隔符
-----------------------------7d226137250336
Content-Disposition: form-data; name="FILE2"; filename=""
Content-Type: application/octet-stream
-----------------------------7d226137250336
從以上文件我們可以看出,HTTP協定在上傳多個檔案時,是將文件全部放到輸入流並以一定的分隔符號來區分的。實際上upload()方法就是要分析上面的文件,確定分隔符號的內容、各個文件的內容格式、文件的完整路徑名稱、及其文件的實際資料的始末位置。這裡需要說明的一點是分隔符號是隨機的,它是傳輸流檔案的第一個回車符之前的所有字元。
Upload()方法的實作流程是:首先將輸入流檔案輸出到位元組數組m_binArray中,透過下面的程式碼實作。
m_totalBytes=1024;totalRead=0;
for(; totalRead < m_totalBytes; totalRead += readBytes)
try
{ m_request.getInputStream();
readBytes = m_request.getInputStream().read(m_binArray, totalRead, m_totalBytes - totalRead);
}catch(Exception e){ throw new SmartUploadException("Unable to upload.");}
這裡採用了循環中多字節讀取方法,以上循環不斷地讀取資料直到數組填滿為止。如果一個檔案可以完全得到,則檔案的所有位元組也就可以全部得到。但因為網路速度通常比CPU慢得多,所以程式很容易在所有的資料到來之前就清空網路緩衝區。實際上,多位元組讀取方法在試圖從暫時為空但是開放的網路快取區讀取資料時,該方法會傳回0,這表示沒有資料存在但網路流沒有關閉。在這種情況下,單字節方法將阻止執行程式的執行,所以多位元組的行為優於單字節read()方法的行為。接下來將分析位元組數組m_binArray。首先找到分隔符號;使用getDataHeader()方法傳回檔案資訊頭的值,從中確定來源檔案的完整路徑名稱、來源檔案的副檔名和來源檔案檔案內容格式;使用getDataSection()方法傳回檔案的內容數據,並記錄檔案資料在位元組數組中的起止位置。然後產生一個File類別實例,並將檔案的完整路徑名稱、來源檔案的副檔名、來源檔案檔案內容格式和檔案的內容資料的起止位置放到File類別實例的屬性中。找到下一個分隔符,繼續重複上述過程,直至分析完畢。
2 採用FTP協定實作多個檔案的上傳
FTP協定是Internet上用來傳送檔案的協議,規定了Internet上檔案互相傳送的標準。在java中實現這項功能是藉助FtpClient類別完成的。具體實現過程:首先與FTP伺服器建立連接;初始化檔案的傳輸方式,包括ASCII和BINARY兩種方式;將檔案輸出到檔案輸入流FileInputStream中;FileInputStream中的資料讀入位元組數組中;位元組數組中的資料寫入輸出流TelnetOutputStream(利用write方法將資料寫入到一個網路連結)。這樣和來源檔案同名的一個檔案就複製到了伺服器端。本範例的JavaBean中透過connectServer()、upload()和closeConnect()三個方法完成檔案上傳過程。主要實作如下:
public class ftpUpload
{ String filename;String filename1;FtpClient ftpClient;
public void connectServer(string server,string user,string password,string path)
{
//server:FTP伺服器的IP位址;user:登入FTP伺服器的使用者名稱
//password:登入FTP伺服器的使用者名稱的口令;path:FTP伺服器上的路徑
try{ ftpClient=new FtpClient();
ftpClient.openServer(server);
ftpClient.login(user, password);
System.out.println("login success!");
if (path.length()!=0) ftpClient.cd(path);
ftpClient.binary(); }catch (IOException ex) {System.out.println(ex);}
}
public void closeConnect()
{try{ ftpClient.closeServer();
}catch (IOException ex) {System.out.println(ex);}
}
public void upload()
{ filename1=findFileName(filename);
//從filename分析出檔案的名稱,作為目標檔案的名稱,具體方法實現未給出
try {
TelnetOutputStream os=ftpClient.put(filename1);
java.io.File file_in=new java.io.File(filename);
FileInputStream is=new FileInputStream(file_in);
byte[] bytes=new byte[1024];
int c;
while ((c=is.read(bytes))!=-1){ os.write(bytes,0,c); }
is.close(); os.close();
} catch (IOException ex) {System.out.println(ex);}
}
}
connectServer()完成與FTP伺服器建立連線的功能,使用FtpClient的openServer(string server)方法開啟遠端FTP伺服器,然後使用FtpClient的login(user, password)方法登入伺服器。登入遠端FTP伺服器有兩種方式,一種是註冊用戶登錄,另一種是以匿名方式登入。前者要求用戶先註冊為伺服器的客戶,伺服器會給客戶一個登入帳號和密碼,依據帳號和密碼連結到伺服器上。後者要求使用者不用註冊而使用特殊的使用者名稱"annoymous"和"guest"有限制的存取遠端主機的公開文件,現在許多系統要求使用者將Email位址作為口令。出於安全的目的,大部分匿名FTP主機一般只允許遠端使用者下載文件,而不允許上傳,這將依賴FTP伺服器的設定。使用者可依實際情況選擇使用兩種方式。登入完成後使用FtpClient的binary()方法初始化傳輸方式為位元組方式。 upload()完成檔案的上傳功能。創建來源檔案的檔案輸入流FileInputStream,將輸入流寫入到位元組數組中,利用TelnetOutputStream的write方法將位元組數組中的資料寫入到一個網路連結上。由於TelnetOutputStream打開的是FTP伺服器上的一個文件,所以資料寫入到了目標文件中,這樣就完成了文件上傳。 closeConnect()要求與伺服器斷開連線。
以上只是單一檔案上傳的過程,如果是多個檔案可以多次呼叫此上傳過程。由以上兩種方式我們可以看出採用FTP協定實作多個檔案的上傳比較簡單,容易實現。利用FTP協定上傳檔案一般是編寫的客戶端的程序,伺服器端的安全設定會比較複雜;而利用HTTP協定上傳檔案則是伺服器端的應用程序,相對來說安全設定會比較簡單。而透過測試發現FTP上傳方式在傳輸大檔案時速度是HTTP上傳方式的數十倍甚至幾百倍,但在傳輸小於1M的檔案時卻比HTTP上傳方式稍慢一些。所以說兩種傳輸方式各有優勢,請讀者依照自身狀況量力而行。如果有任何問題或需要其他部分的源碼,請與我聯絡!