利用nginx實現文件上傳和進度條功能

最近做一個產品,需要實現從網頁上傳文件給服務器。一般情況下都是採用Ajax異步方式,創建一個iframe,在iframe裏面把數據以form方式提交給後端的服務器腳本,由服務器腳本(比如PHP)來負責接收上傳的數據。這種方式存在性能和效率的問題。所以,決定採用Nginx的上傳模塊來完成接收數據的功能,接收完數據後,再去轉給後端腳本語言進行後續處理(比如:移動文件、插入文件的信息到數據庫中)。同時,由於需要在前端展現上傳的進度,因此可以利用Nginx一個uploadprogress模塊來獲取。
     整個處理框圖如下:

     實現步驟:
     1、查看Nginx是否安裝了這兩個模塊(nginx_upload_modulenginx_uploadprogress_module),命令nginx -V (注意是大寫),可以查看Nginx當時編譯時候的參數,如果發現有上述兩個模塊,說明Nginx已經安裝了這兩個模塊。如果沒有的話,就需要安裝這兩個Nginx模塊。由於這兩個模塊不在Nginx源代碼中,需要重新編譯Nginx,在編譯選項中加上
    --add-module=/模塊源代碼路徑/nginx_upload_module-2.2.0 --add-module=/模塊源代碼路徑/nginx_uploadprogress_module-0.8.2 。
      2、由於產品的前端使用的是jQuery框架,所以,找了一個現成的jQuery下的上傳文件插件(ajaxfileupload)。該代碼基本原理就是動態創建一個iframe,在iframe中再增加一個form,最後數據放到這個form中提交給服務器,代碼量比較小也就200來行代碼。前端的代碼如下:
  1. <script type="text/javascript" src="http://192.168.1.203:7100/js/libs/ajaxfileupload.js" ></script>  
  2. <script type="text/javascript">  
  3.     function uploadfile(){          
  4.         $.ajaxFileUpload({  
  5.             url:'http://192.168.1.203:7100/upload/‘,//上傳的地址  
  6.             sercureuri:false,  
  7.             fileElementId:'fileToUpload',  
  8.             dataType:'json',  
  9.             success:function(data,status){  
  10.                 if(typeof(data.error) != 'undefined'){  
  11.                     if(data.error != '')  
  12.                         alert(data.error);  
  13.                 }  
  14.                 else{  
  15.                     alert(data.msg);  
  16.                 }  
  17.             },  
  18.             error:function(data,status, e){  
  19.                 alert(e);  
  20.             }  
  21.         });  
  22.         return false;  
  23.     }     
  24. </script>  
  25. <div>  
  26. <input type="file" name="addfile" size="50" id="fileToUpload" />  
  27. <input type="button" value="上傳" onclick="return uploadfile();"/>  
  28. </div>  


其中,success的回調函數參數是服務器返給瀏覽器的結果。
3、配置Nginx,實現上傳模塊來接收頁面上傳的文件。把下面配置添加到Nginx的配置文件中,注意是加在server的上下文中。
        location = /upload {
                upload_pass     /service.php?path=uploadfile&a=upload_server;//表示Nginx接收完上傳的文件後,然後交給後端處理的地址
                upload_cleanup 400 404 499 500-505; //表示當發生這些http status代碼的情況下,會把上傳的文件刪除
                upload_store    /tmp/upload_tmp 1;//上傳模塊接收到的文件臨時存放的路徑, 1 表示方式,該方式是需要在/tmp/upload_tmp下創建以0到9爲目錄名稱的目錄,上傳時候會進行一個散列處理。
                upload_store_access user:r; //指定訪問模式
                upload_limit_rate 128k; //設定上傳速度上限
                upload_set_form_field "${upload_field_name}_name" $upload_file_name; //設定後續腳本語言訪問的變量,其中${upload_field_name}對照本例子就是addfile。比如後臺PHP就可以通過$_POST['addfile_name']來獲取上傳文件的名稱。
                upload_set_form_field "${upload_field_name}_content_type" $upload_content_type;//同上
                upload_set_form_field "${upload_field_name}_path" $upload_tmp_path;//由於在upload_store設置了臨時文件存放根路徑,該路徑就是經過散裂後上傳文件存在真實路徑,比如後續處理可以根據這值把上傳文件拷貝或者移動到指定的目錄下。
                upload_pass_form_field "^.*$";//
                upload_pass_args on;// 打開開關,意思就是把前端腳本請求的參數會傳給後端的腳本語言,比如:http://192.168.1.203:7100/upload/?k=23.PHP腳本可以通過$_POST['k']來訪問。
        }
4、上述配置完了,就可以實現上傳的功能了。但是,要獲取上傳的進度,那還是需要配置另外一個模塊nginx_uploadprogress_module。其實,獲取當前進度原理比較簡單,就是通過javascript以異步方式定時給特定地址發送請求,這個模塊會以json格式返回上傳的進度。配置比較簡單。
          1)、首先打開這個模塊功能,在Nginx配置文件中http上下文裏面,增加upload_progress proxied 5m;其中,proxied表示名稱(zone_name官方文檔),5m表示每次鏈接存放跟蹤信息的大小。另外,再設置返回格式爲json,upload_progress_json_output;
          2)、在上述的location = /upload中增加一個配置項track_uploads proxied 30s; 其中,proxied就是剛纔在第一步設置的名字,30s表示每次鏈接處理完畢後,鏈接會保持30s。
          3)、設置一個location來處理javascript發送請求。
          location ^~ /progress {

  report_uploads proxied;    #GET此地址得到上傳進度
}
           4)、還有一個參數考慮設置upload_progress_header ,這個值缺省是X-Progress-ID。有點類似SessionID,主要用在前臺需要在上傳文件的時候需要設置這個參數值,比如設置爲uuid值。這樣javascript每次發送請求要獲取上傳進度時候,都需要帶上這個參數,這樣上傳進度跟蹤模塊才知道是返回那個鏈接的進度。
           經過這三步驟,就把上傳進度跟蹤模塊配置好了。現在就需要對前臺腳本就行修改
5、修改第2步的前臺腳本
  1. <script type="text/javascript" src="http://192.168.1.203:7100/js/libs/ajaxfileupload.js" ></script>  
  2. <script type="text/javascript">  
  3.     <strong>var interval = undefined;</strong>  
  4.     function uploadfile(){    
  5.         var uuid = "";  
  6.         for (var i = 0; i < 32; i++) {  
  7.             uuid += Math.floor(Math.random() *16).toString(16);  
  8.         }        
  9.         $.ajaxFileUpload({  
  10.             url:'http://192.168.1.203:7100/upload/<strong>?X-Progress-ID=' + uuid</strong>,//上傳的地址  
  11.             sercureuri:false,  
  12.             fileElementId:'fileToUpload',  
  13.             dataType:'json',  
  14.             success:function(data,status){  
  15.                 if(typeof(data.error) != 'undefined'){  
  16.                     if(data.error != '')  
  17.                         alert(data.error);  
  18.                 }  
  19.                 else{  
  20.                     alert(data.msg);  
  21.                 }  
  22.             },  
  23.             error:function(data,status, e){  
  24.                 alert(e);  
  25.             }  
  26.         });  
  27.       <strong>  interval = window.setInterval(  
  28.            function () {  
  29.              getUploadProgress(uuid);  
  30.            },  
  31.            2000  
  32.          );</strong>  
  33.         return false;  
  34.     }     
  35.  <strong>   function getUploadProgress(uuid){  
  36.       etajax.sendRequest('http://192.168.1.203:7100/progress/',"GET","X-Progress-ID=" + uuid,getUploadProgressCallback);  
  37.     }  
  38.     function getUploadProgressCallback(type, json, http){  
  39.         if(type == "load"){  
  40.             var bar = document.getElementById('tp');  
  41.             if(json.state == "uploading"){  
  42.                    
  43.                  var w =  Math.floor(json.received * 100.0 / json.size) ;  
  44.                  bar.innerHTML = w + "%";  
  45.             }  
  46.                 /* we are done, stop the interval */  
  47.              if (json.state == 'done') {  
  48.                 bar.innerHTML = "100%";  
  49.                  window.clearTimeout(interval);  
  50.             }  
  51.         }  
  52.     }     </strong>  
  53. </script>  
  54. <div>  
  55. <input type="file" name="addfile" size="50" id="fileToUpload" />  
  56. <input type="button" value="上傳" onclick="return uploadfile();"/>  
  57. </div>  
  58. <strong><div>  
  59.     <div id="tp">0%</div>  
  60. </div></strong>  
上述黑體就是增加的代碼,其中,有些函數是調用產品封裝好的函數,所以,不要全部照搬。主要是抓住以下幾個要點就可以了:
    1、在上傳文件時候需要增加一個uuid,對應的參數就是upload_progress_header設置的,缺省是X-Progress-ID。
    2、在請求獲取進度的時候,都要帶上這個uuid。
    3、設定一個定時期,定時發送異步的GET方式請求,獲取進度數據。
    4、返回的json格式是{"state":"uploading", "size":3232,"received":34},其中上傳完畢state值爲done,如果發生錯誤會,state就是error,並且會返回status,錯誤編碼。
    
    步驟就介紹在這裏了。另外,javascript方式上傳文件,在用戶上傳文件時候,最好能獲取上傳文件大小,這樣可以提前告訴用戶是否超出允許上傳的大小值。但是,目前javascript方式獲取文件大小要兼容所有瀏覽器還是存在問題。我打算還是寫個flash,通過flash方式來獲取,比較保險。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章