1. 程式人生 > >如何實現支援大檔案的高效能HTTP檔案上傳伺服器

如何實現支援大檔案的高效能HTTP檔案上傳伺服器

       HTTP檔案上傳是做Web開發時的常見功能,例如上傳圖片、上傳影片等。實現HTTP檔案上傳也比較簡單,用任何Web端的指令碼都可以輕鬆實現,例如PHP、JSP都有現成的函式或者類來呼叫。但筆者最近在做專案時遇到了一個大問題,專案需要上傳視訊檔案,這些視訊檔案的尺寸一般大於2GB,用PHP開發時,將伺服器端的上傳尺寸設定得足夠大,但用Chrome、FirFox等瀏覽器上傳時,經常出現響應超時而上傳失敗,懷疑是PHP的問題,後改用JSP來實現檔案上傳,現象也一樣,經過分析後發現,原來PHP、JAVA的上傳是先由伺服器快取為臨時檔案,或者伺服器將上傳資料快取到記憶體中後,再由指令碼呼叫相關的上傳檔案處理函式來移動臨時檔案來儲存檔案資料;由於PHP、JAVA等處理檔案上傳需要分兩步,對於大檔案與超大檔案來說, 再次移動檔案也是比較耗時間與系統資源的,由於瀏覽器將檔案提交到伺服器上後就會等待伺服器端的響應,伺服器端移動檔案耗時太長,導致瀏覽器等待超時而報錯。看來採用這種方式無法解決這種問題,於是決定重新自己實現一個獨立HTTP檔案長傳伺服器來供Web應用系統呼叫。

一、HTTP檔案上傳的技術原理 HTTP檔案上傳是通過  multipart/form-data 協議實現的,multipart/form-data實際上是一種資料的編碼分割方式,例如在瀏覽器端編寫一個檔案上傳的頁面,向伺服器傳送POST請求後,伺服器端將會收到如下資料:

POST /douploadfile HTTP/1.1 Host: www.melody.com:8068 Connection: keep-alive Content-Length: 510 Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*.*;q=0.8 Origin: http://www.melody.com:8068 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4Ze9aQ5NKoUihnkZ Referer: http://www.melody.com:8068/douploadfile Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8 Cookie: aa_videos_per_page=30; guest_name_43b2525ed9f77823e78012a4f24405a5=admin

------WebKitFormBoundary4Ze9aQ5NKoUihnkZ Content-Disposition: form-data; name="file1"; filename="file1.c" Content-Type: text/plain

1111111111111111111111111111111111111111 ------WebKitFormBoundary4Ze9aQ5NKoUihnkZ Content-Disposition: form-data; name="file2"; filename="file2.c" Content-Type: text/plain

22222222222222222222222222222222222222222222222222 ------WebKitFormBoundary4Ze9aQ5NKoUihnkZ Content-Disposition: form-data; name="text1"

ABCDABCD ------WebKitFormBoundary4Ze9aQ5NKoUihnkZ--

multipart/form-data 協議將檔案資料與Form的表單資料一起傳輸,通過分割符來區分表單域與檔案資料,這個分割符是在HTTP請求頭裡指定,例如本處的分割符為 boundary=----WebKitFormBoundary4Ze9aQ5NKoUihnkZ,每個分割符前有個分隔符頭“--”,最後一個分割符也後也必須新增“--”。只要知道HTTP上傳檔案的資料格式,就好實現檔案上傳伺服器了, 只要在接收完畢HTTP請求頭後,將後續的資料通過分割符區分即可,如果是檔案資料,分隔符後的附加資料會指定 filename名。

二、技術實現 出於效能考慮,筆者實現的HTTP檔案上傳伺服器採用C語言實現,檔案上傳伺服器是作為獨立程序來執行的。採用獨立檔案上傳伺服器上傳檔案的過程是這樣的: 1)瀏覽器將檔案上傳請求提交到 檔案上傳伺服器。 2)檔案上傳伺服器在接收檔案完畢後將檔案的資訊例如長度,物理位置與訪問URL返回給瀏覽器,通過JSON方式即可。 3)瀏覽器接收到上傳的反饋資訊後暫存,然後再與其它資訊一起提交到Web伺服器。

由於檔案上傳伺服器與Web伺服器不是同一程序,監控的TCP埠不同,在向上傳伺服器發起POST請求時,瀏覽器將會認為是不同的域,會出現跨域問題,瀏覽器在遇到跨域訪問時,一般會先發送OPTION請求,上傳伺服器需要對跨域做出正確的響應,在響應頭裡加入 Access-Control-Allow-Origin: * 即可。

檔案上傳伺服器採用JSON來反饋資訊。

檔案上傳伺服器採用執行緒SELECT模型,支援1000個併發連線,支援大於2GB的檔案上傳,採用即時寫入技術,不需要再次移動檔案。

採用無重新整理介面方式來上傳檔案,即傳送上傳請求後,主介面無需重新整理。

檔案上傳伺服器支援HTML5方式長傳,並能夠顯示上傳進度,由於這是通過瀏覽器本身來實現的,比較容易。 檔案上傳伺服器支援傳統瀏覽器(例如IE8或者以下版本)上傳資料,也能夠顯示進度,通過AJAX定時向伺服器傳送進度查詢請求來實現。瀏覽器在發起上傳請求時,制定一個上傳ID,然後定時通過這個ID來獲取上傳進度。

由於上傳伺服器的程式碼足夠多,因此不一一闡述,請檢視附件的檔案上傳伺服器樣本。

三、瀏覽器端呼叫 .1 HTML5檔案上傳呼叫 HTML5上傳是採用 HTML5的FormData 與XMLHttpRequest來實現的,具體如何操作請查閱相關資 料。 步驟如下:

 1)建立FormData並將原始form表單的DOM元素作為一個引數傳遞給FormData; 

2)建立XMLHttpRequest物件並設定好引數 

3)呼叫XMLHttpRequest物件的open方法並制定正確的URL,這個URL應該是上傳伺服器的接受地址。

4)呼叫XMLHttpRequest的send方法。 示範程式碼如下

var myForm = document.getElementById("form1"); 

var fd = new FormData(myForm);    

var xhr = new XMLHttpRequest();  

xhr.upload.addEventListener("progress", uploadProgress, false);  

xhr.overrideMimeType("application/octet-stream");  

 xhr.addEventListener("load", uploadComplete, false); 

 xhr.addEventListener("error", uploadFailed, false);  

xhr.addEventListener("abort", uploadCanceled, false);  

xhr.open("POST", upload_url,true);  

xhr.send(fd);

2 IFRAME檔案上傳呼叫 由於微軟微軟的IE8及以下瀏覽器不支援HTML5,我們舊的採用傳統的方式來上傳檔案。為了實現界 面的優雅設計,即無重新整理方式上傳檔案,在進行傳統方式上傳檔案時,將 form 的 target 屬性指向一個 iframe,瀏覽器將在上傳完畢後將返回網頁輸出到這個iframe,通過將這個iframe隱藏,可以避免破壞界 面的美觀。 iframe方式上傳檔案的步驟如下: 

1)構建一個隱藏的iframe。 

2)設定form的target為網頁裡的一個iframe的名字一邊指向這個iframe。 

)用普通的submit提交表單。

 4)從iframe裡獲取返回資訊 iframe方式也適用於現代瀏覽器,包括移動裝置瀏覽器。

採用iframe方式提交資料後,在資料提交結束並獲得反饋後,會觸發iframe的load事件,我們通過設 置ifame的load事件來獲得反饋。由於訪問 iframe與應用伺服器仍舊是不同的域,需要繞過這個跨域訪問 限制,iframe的name屬性可以作為資料互動的紐帶。HYFileServer伺服器通過產生相關程式碼來修改iframe 的name屬性,父框架通過訪問iframe的name屬性來獲得反饋資訊。

如果需要測試,可以通過 github來下載 這個檔案上傳伺服器

https://github.com/wenshui2008/UploadServer