1. 程式人生 > >web 檔案上傳的幾種方式

web 檔案上傳的幾種方式

問題

檔案上傳在WEB開發中應用很廣泛。

檔案上傳是指將本地圖片、視訊、音訊等檔案上傳到伺服器上,可以供其他使用者瀏覽或下載的過程。

以下總結了常見的檔案(圖片)上傳的方式和要點處理。

表單上傳

這是傳統的form表單上傳,使用form表單的input[type=”file”]控制元件,可以開啟系統的檔案選擇對話方塊,從而達到選擇檔案並上傳的目的,它的好處是多瀏覽器相容,它是web開發者最常用的一種檔案上傳方式。

表單的程式碼如下:

<form method="post" action="http://uploadUrl" enctype="multipart/form-data">
    <input name="file" type="file" accept="image/gif,image.jpg" />
    <input name="token" type="hidden" />
    <input type="submit" value="提交" /> 
</form>

 

以下是表單上傳幾個關鍵點:

method="post": 採用post方式提交資料
enctype="multipart/form- data":採用multipart格式上傳檔案,此時request頭會顯示 Content-Type:multipart/form-data; boundary=—-WebKitFormBoundaryzr34cwJ67R95KQC9
action:標明上傳的服務端處理地址
type="file":使用input的file控制元件上傳
如果是多檔案批量上傳,可以將input[type=”file”]的name屬性設定為如:name=”file[]”
accept

屬性是HTML5的新屬性,它規定了可通過檔案
上傳提交的檔案型別

上傳的觸發事件可以是:input[type=”file”]的onChange觸發,也可以由一個獨立的按鈕的onClick使整個表單提交,此時還可以用input[type="hidden"]帶一些其它的引數,比如Token來源驗證等等。

Ajax無重新整理上傳

Ajax無重新整理上傳的方式,本質上與表單上傳無異,只是把表單裡的內容提出來採用ajax提交,並且由前端決定請求結果回傳後的展示結果,不用像直接表單上傳那樣重新整理和跳轉頁面。在這裡,我們採用jQuery來作為操作DOM和建立ajax提交的js基礎庫。

html程式碼片段如下:

1 2 3 4 < form >      < input  id="file" name="file" type="file" />      < input  id="token" name="token" type="hidden" /> </ form >

 

javascript程式碼片段如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $( "#file" ).on( "change" function (){    var  formData =  new  FormData();    formData.append( "file" , $( "#file" )[0].files);    formData.append( "token" , $( "#token" ).val());    $.ajax({        url:  "http://uploadUrl" ,        type:  "POST" ,        data: formData,        processData:  false ,        contentType:  false ,        success:  function (response){                // 根據返回結果指定介面操作        }    }); });

 

我們使用了file控制元件的change來觸發上傳事件,當然你也可以使用某個按鈕來觸發表單提交。提交資料時,用到了FormData物件來發送二進位制檔案,FormData建構函式提供的append()方法,除了直接新增二進位制檔案還可以附帶一些其它的引數, 作為XMLHttpRequest例項的引數提交給服務端。

使用jQuery提供的ajax方法來發送二進位制檔案,還需要附加兩個引數:

processData: false // 不要對data引數進行序列化處理,預設為true
contentType: false // 不要設定Content-Type請求頭,因為檔案資料是以 multipart/form-data 來編碼

Flash上傳

很多時候上傳的需求要求顯示上傳進度、中斷上傳過程、大檔案分片上傳等等,這時傳統的表單上傳很難實現這些功能,於是產生了使用Flash上傳的方式,它採用Flash作為一箇中間代理層,代替客戶端跟服務端通訊,此外,它也對客戶端的檔案選擇方面擁有更多的控制權,比input[type=”file”] 要大得多。

在這裡我使用了jQuery封裝好的uploadify外掛來進行演示,一般這類外掛都自帶了上傳用的Flash檔案,因為跟服務端回傳的資料和展示跟客戶端的互動,都是Flash檔案的介面跟外掛來對接的。

<div id="file_upload"></div>

 

html部分很簡單,預留一個hook後,外掛會在這個節點內部建立Flash的object,並且還附帶建立了上傳進度、取消控制元件和多檔案佇列展示等介面。

複製程式碼
$(function() {
  $("#file_upload").uploadify({
      auto: true,
      method: "Post",
      width: 120,
      height: 30,
      swf: './uploadify.swf',
      uploader: 'http://uploadUrl',
      formData: {
          token: $("#token").val()
      },
      fileObjName: "file",
      onUploadSuccess: function(file, data, response){
          // 根據返回結果指定介面操作
      }
  });
});
複製程式碼

 

關於jQuery.uploadify可以訪問了解: http://www.uploadify.com/documentation/ 。值得注意的是flash並不適合手機端應用,更好的解決方案是使用flash+html5來解決平臺的相容性問題。

截圖貼上上傳

我們發現現在有好多上傳應用已經提供了截圖貼上上傳功能,如WebUploader,它就支援QQ截圖然後貼上上傳。

首先,截圖貼上上傳的核心思想是,監聽貼上事件,然後獲取剪下板中的資料,如果是一張圖片,則觸發上傳事件。

程式碼片段如下:

複製程式碼
$("textarea").on("paste", function(e){
   e.stopPropagation();
   var self = this;
   var clipboardData = e.originalEvent.clipboardData;
   if (clipboardData.items.length <= 0) {
       return;
   }
   var file = clipboardData.items[0].getAsFile();
   if (!file) {
       return;
   }
   var formData = new FormData();
   formData.append("file", file);
   formData.append("token", $("#token").val());
   $.ajax({
       url: "http://uploadUrl",
       type: "POST",
       data: formData,
   }).done(function(response) {
       // 根據返回結果指定介面操作
   });
   e.preventDefault();
});
複製程式碼

 

從上面程式碼可以看出,上傳的過程都是一樣的,主要是獲取檔案的方式。 當進行貼上(右鍵paste/ctrl+v)操作時,觸發剪貼簿事件’paste’,從系統剪下板獲取內容,而系統剪下板的資料在不同瀏覽器儲存在不同的位置:

IE核心:windows.clipboardData
其它:e.originalEvent.clipboardData

拖拽上傳

拖拽上傳的方式,支援的瀏覽器比較少,因為它用到了HTML5的兩個新的屬性(API)一個是Drag and Drop,一個是File API

上傳域監聽拖拽的三個事件:dragEnterdragOverdrop,分別對應拖拽至、拖拽時和釋放三個操作的處理機制,當然你也可以監聽dragLeave事件。

HTML5的File API提供了一個FileList的介面,它可以通過拖拽事件的e.dataTransfer.files來傳遞的檔案資訊,獲取本地檔案列表資訊。

File API在HTML5規範中只是草案,在 W3C 草案中,File 物件只包含檔名、檔案型別和檔案大小等只讀屬性。但部分瀏覽器在草案之外提供了一個名為 FileReader 的物件,用以讀取檔案內容,並且可以監控讀取狀態,它提供的方法有: “readAsBinaryString” ,”readAsDataURL” ,”readAsText” ,”abort” 等。

程式碼片段如下:

複製程式碼
// dragenter
$("#textarea").on("dragenter", function(e){
    e.preventDefault();
});
// dragover
$("#textarea").on("dragover", function(e){
    e.preventDefault();
});
// drop
$("#textarea").on("drop", function(e){
    e.stopPropagation();
    e.preventDefault();
    var files = e.originalEvent.dataTransfer.files;
    _.each(files, function(file) {
        if (!/^image*/.test(file.type)) {
          return;
        }
        var fileReader = new FileReader();
        fileReader.onload = function() {
          //uploadFile(file);
        };
        fileReader.readAsDataURL(file);
    });
});
複製程式碼

 

拖拽上傳過程中的幾個關鍵點:

在drop事件觸發後通過e.dataTransfer.files獲取拖拽檔案列表,在jQuery中是e.originalEvent.dataTransfer.files
拖拽上傳僅支援圖片,檔案物件中file.type標識了檔案型別。
由於可能是多圖拖拽,所以可以遍歷圖片上傳,這裡用了Underscore的each方法。
這裡用readAsDataURL讀取檔案內容為二進位制檔案,你還可以將其轉換為Base64方式上傳,只是http協議裡面存在對非二進位制資料的上傳大小限制為2M。
上傳的過程跟前面的方式相同,即:建立FormData物件併發起Ajax請求。
拍照上傳

拍照上傳可以是PC上的攝像頭拍照上傳也可以是手機等移動裝置的拍照上傳。手機上的拍照上傳最常見就是我們使用微信發照片了。

手機實現拍照上傳的程式碼:

<input type=file accept="image/*">
<input type=file accept="video/*">

ios 有拍照、錄影、選取本地圖片功能,部分android只有選取本地圖片功能。

上傳與安全

上傳檔案時必須做好檔案的安全性,除了前端必要的驗證,如檔案型別、字尾、大小等驗證,重要的還是要在後臺做安全策略。

這裡我列舉幾個注意點:

  • 後臺需要進行檔案型別、大小、來源等驗證
  • 定義一個.htaccess檔案,只允許訪問指定副檔名的檔案。
  • 將上傳後的檔案生成一個隨機的檔名,並且加上此前生成的副檔名。
  • 設定上傳目錄執行許可權,避免不懷好意的人繞過如圖片副檔名進行惡意攻擊,拒絕指令碼執行的可能性。
  分類:  檔案上傳

問題

檔案上傳在WEB開發中應用很廣泛。

檔案上傳是指將本地圖片、視訊、音訊等檔案上傳到伺服器上,可以供其他使用者瀏覽或下載的過程。

以下總結了常見的檔案(圖片)上傳的方式和要點處理。

表單上傳

這是傳統的form表單上傳,使用form表單的input[type=”file”]控制元件,可以開啟系統的檔案選擇對話方塊,從而達到選擇檔案並上傳的目的,它的好處是多瀏覽器相容,它是web開發者最常用的一種檔案上傳方式。

表單的程式碼如下:

<form method="post" action="http://uploadUrl" enctype="multipart/form-data">
    <input name="file" type="file" accept="image/gif,image.jpg" />
    <input name="token" type="hidden" />
    <input type="submit" value="提交" /> 
</form>

 

以下是表單上傳幾個關鍵點:

method="post": 採用post方式提交資料
enctype="multipart/form- data":採用multipart格式上傳檔案,此時request頭會顯示 Content-Type:multipart/form-data; boundary=—-WebKitFormBoundaryzr34cwJ67R95KQC9
action:標明上傳的服務端處理地址
type="file":使用input的file控制元件上傳
如果是多檔案批量上傳,可以將input[type=”file”]的name屬性設定為如:name=”file[]”
accept屬性是HTML5的新屬性,它規定了可通過檔案
上傳提交的檔案型別

上傳的觸發事件可以是:input[type=”file”]的onChange觸發,也可以由一個獨立的按鈕的onClick使整個表單提交,此時還可以用input[type="hidden"]帶一些其它的引數,比如Token來源驗證等等。

Ajax無重新整理上傳

Ajax無重新整理上傳的方式,本質上與表單上傳無異,只是把表單裡的內容提出來採用ajax提交,並且由前端決定請求結果回傳後的展示結果,不用像直接表單上傳那樣重新整理和跳轉頁面。在這裡,我們採用jQuery來作為操作DOM和建立ajax提交的js基礎庫。

html程式碼片段如下:

1 2 3 4 < form >      < input  id="file" name="file" type="file" />      < input  id="token" name="token" type="hidden" /> </ form >

 

javascript程式碼片段如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $( "#file" ).on( "change" function (){    var  formData =  new  FormData();    formData.append( "file" , $( "#file" )[0].files);    formData.append( "token" , $( "#token" ).val());    $.ajax({        url:  "http://uploadUrl" ,        type:  "POST" ,        data: formData,        processData:  false ,        contentType:  false ,        success:  function (response){                // 根據返回結果指定介面操作        }    }); });

 

我們使用了file控制元件的change來觸發上傳事件,當然你也可以使用某個按鈕來觸發表單提交。提交資料時,用到了FormData物件來發送二進位制檔案,FormData建構函式提供的append()方法,除了直接新增二進位制檔案還可以附帶一些其它的引數, 作為XMLHttpRequest例項的引數提交給服務端。

使用jQuery提供的ajax方法來發送二進位制檔案,還需要附加兩個引數:

processData: false // 不要對data引數進行序列化處理,預設為true
contentType: false // 不要設定Content-Type請求頭,因為檔案資料是以 multipart/form-data 來編碼

Flash上傳

很多時候上傳的需求要求顯示上傳進度、中斷上傳過程、大檔案分片上傳等等,這時傳統的表單上傳很難實現這些功能,於是產生了使用Flash上傳的方式,它採用Flash作為一箇中間代理層,代替客戶端跟服務端通訊,此外,它也對客戶端的檔案選擇方面擁有更多的控制權,比input[type=”file”] 要大得多。

在這裡我使用了jQuery封裝好的uploadify外掛來進行演示,一般這類外掛都自帶了上傳用的Flash檔案,因為跟服務端回傳的資料和展示跟客戶端的互動,都是Flash檔案的介面跟外掛來對接的。

<div id="file_upload"></div>

 

html部分很簡單,預留一個hook後,外掛會在這個節點內部建立Flash的object,並且還附帶建立了上傳進度、取消控制元件和多檔案佇列展示等介面。

複製程式碼
$(function() {
  $("#file_upload").uploadify({
      auto: true,
      method: "Post",
      width: 120,
      height: 30,
      swf: './uploadify.swf',
      uploader: 'http://uploadUrl',
      formData: {
          token: $("#token").val()
      },
      fileObjName: "file",
      onUploadSuccess: function(file, data, response){
          // 根據返回結果指定介面操作
      }
  });
});
複製程式碼

 

關於jQuery.uploadify可以訪問了解: http://www.uploadify.com/documentation/ 。值得注意的是flash並不適合手機端應用,更好的解決方案是使用flash+html5來解決平臺的相容性問題。

截圖貼上上傳

我們發現現在有好多上傳應用已經提供了截圖貼上上傳功能,如WebUploader,它就支援QQ截圖然後貼上上傳。

首先,截圖貼上上傳的核心思想是,監聽貼上事件,然後獲取剪下板中的資料,如果是一張圖片,則觸發上傳事件。

程式碼片段如下:

複製程式碼
$("textarea").on("paste", function(e){
   e.stopPropagation();
   var self = this;
   var clipboardData = e.originalEvent.clipboardData;
   if (clipboardData.items.length <= 0) {
       return;
   }
   var file = clipboardData.items[0].getAsFile();
   if (!file) {
       return;
   }
   var formData = new FormData();
   formData.append("file", file);
   formData.append("token", $("#token").val());
   $.ajax({
       url: "http://uploadUrl",
       type: "POST",
       data: formData,
   }).done(function(response) {
       // 根據返回結果指定介面操作
   });
   e.preventDefault();
});
複製程式碼

 

從上面程式碼可以看出,上傳的過程都是一樣的,主要是獲取檔案的方式。 當進行貼上(右鍵paste/ctrl+v)操作時,觸發剪貼簿事件’paste’,從系統剪下板獲取內容,而系統剪下板的資料在不同瀏覽器儲存在不同的位置:

IE核心:windows.clipboardData
其它:e.originalEvent.clipboardData

拖拽上傳

拖拽上傳的方式,支援的瀏覽器比較少,因為它用到了HTML5的兩個新的屬性(API)一個是Drag and Drop,一個是File API

上傳域監聽拖拽的三個事件:dragEnterdragOverdrop,分別對應拖拽至、拖拽時和釋放三個操作的處理機制,當然你也可以監聽dragLeave事件。

HTML5的File API提供了一個FileList的介面,它可以通過拖拽事件的e.dataTransfer.files來傳遞的檔案資訊,獲取本地檔案列表資訊。

File API在HTML5規範中只是草案,在 W3C 草案中,File 物件只包含檔名、檔案型別和檔案大小等只讀屬性。但部分瀏覽器在草案之外提供了一個名為 FileReader 的物件,用以讀取檔案內容,並且可以監控讀取狀態,它提供的方法有: “readAsBinaryString” ,”readAsDataURL” ,”readAsText” ,”abort” 等。

程式碼片段如下:

複製程式碼
// dragenter
$("#textarea").on("dragenter", function(e){
    e.preventDefault();
});
// dragover
$("#textarea").on("dragover", function(e){
    e.preventDefault();
});
// drop
$("#textarea").on("drop", function(e){
    e.stopPropagation();
    e.preventDefault();
    var files = e.originalEvent.dataTransfer.files;
    _.each(files, function(file) {
        if (!/^image*/.test(file.type)) {
          return;
        }
        var fileReader = new FileReader();
        fileReader.onload = function() {
          //uploadFile(file);
        };
        fileReader.readAsDataURL(file);
    });
});
複製程式碼

 

拖拽上傳過程中的幾個關鍵點:

在drop事件觸發後通過e.dataTransfer.files獲取拖拽檔案列表,在jQuery中是e.originalEvent.dataTransfer.files
拖拽上傳僅支援圖片,檔案物件中file.type標識了檔案型別。
由於可能是多圖拖拽,所以可以遍歷圖片上傳,這裡用了Underscore的each方法。
這裡用readAsDataURL讀取檔案內容為二進位制檔案,你還可以將其轉換為Base64方式上傳,只是http協議裡面存在對非二進位制資料的上傳大小限制為2M。
上傳的過程跟前面的方式相同,即:建立FormData物件併發起Ajax請求。
拍照上傳

拍照上傳可以是PC上的攝像頭拍照上傳也可以是手機等移動裝置的拍照上傳。手機上的拍照上傳最常見就是我們使用微信發照片了。

手機實現拍照上傳的程式碼:

<input type=file accept="image/*">
<input type=file accept="video/*">

ios 有拍照、錄影、選取本地圖片功能,部分android只有選取本地圖片功能。

上傳與安全

上傳檔案時必須做好檔案的安全性,除了前端必要的驗證,如檔案型別、字尾、大小等驗證,重要的還是要在後臺做安全策略。

這裡我列舉幾個注意點:

  • 後臺需要進行檔案型別、大小、來源等驗證
  • 定義一個.htaccess檔案,只允許訪問指定副檔名的檔案。
  • 將上傳後的檔案生成一個隨機的檔名,並且加上此前生成的副檔名。
  • 設定上傳目錄執行許可權,避免不懷好意的人繞過如圖片副檔名進行惡意攻擊,拒絕指令碼執行的可能性。