文件上傳是Web開發中的重要話題,最直接和簡單的方式是通過表單直接提交文件。 Harttle認為,我們引入jQuery來進行異步上傳可以獲得更好的用戶體驗。 一方面,在JavaScript中進行異步操作比表單更加靈活; 另一方面,異步上傳也避免了上傳大文件時的頁面長時間卡死。
HTML
一個type=file
的<input>
就可以讓用戶來瀏覽並選擇文件, 一般會把輸入控件放到一個<form>
中,下面的一個簡單的表單:
<form> <input type="file" id="avatar" name="avatar"> <button type="button">保存</button> </form>
但為什么我只能選擇一個文件??給<input>
添加一個multiple
屬性就可以多選了!
<input type="file" id="avatar" name="avatar" multiple>
獲取文件列表
上述的<input>
將會擁有一個叫files
的DOM屬性,包含了所選的文件列表(Array
)。
$('button').click(function(){ var $input = $('#avatar'); // 相當於: $input[0].files, $input.get(0).files var files = $input.prop('files'); console.log(files); });
這個Array
中的每一項都是一個File
對象,它有下面幾個主要屬性:
name
: 文件名,只讀字符串,不包含任何路徑信息.size
: 文件大小,單位為字節,只讀的64位整數.type
: MIME類型,只讀字符串,如果類型未知,則返回空字符串.
見:https://developer.mozilla.org/zh-CN/docs/Using_files_from_web_applications
multipart/form-data
上傳文件比較特殊,其內容是二進制數據,而HTTP提供的是基於文本的通信協議。 這時需要采用multipart/form-data
編碼的HTTP表單。其HTTP消息體格式如下所示:
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="title"
harttle
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="avatar"; filename="harttle.png"
Content-Type: image/png
... content of harttle.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
每個字段由一段boundary string來分隔,瀏覽器保證該boundary string不與內容重復, 因而multipart/form-data
能夠成功編碼二進制數據。
更多關於HTTP表單編碼的細節,請參考:HTTP 表單編碼 enctype。
jQuery上傳文件
這是XMLHttpRequest Level 2提供的FormData
對象可以幫助我們進行二進制文件的 multipart/form-data
編碼:
$('button').click(function(){ var files = $('#avatar').prop('files'); var data = new FormData(); data.append('avatar', files[0]); $.ajax({ url: '/api/upload', type: 'POST', data: data, cache: false, processData: false, contentType: false }); });
url
, type
, data
想必做前端的都很熟悉了,介紹其余三個參數:
cache
cache
設為false
可以禁止瀏覽器對該URL(以及對應的HTTP方法)的緩存。 jQuery通過為URL添加一個冗余參數來實現。
該方法只對GET和HEAD起作用,然而IE8會緩存之前的GET結果來響應POST請求。 這里設置cache: false
是為了兼容IE8。
contentType
jQuery中content-type
默認值為application/x-www-form-urlencoded
, 因此傳給data
參數的對象會默認被轉換為query string(見HTTP 表單編碼 enctype)。
我們不需要jQuery做這個轉換,否則會破壞掉multipart/form-data
的編碼格式。 因此設置contentType: false
來禁止jQuery的轉換操作。
processData
jQuery會將data
對象轉換為字符串來發送HTTP請求,默認情況下會用 application/x-www-form-urlencoded
編碼來進行轉換。 我們設置contentType: false
后該轉換會失敗,因此設置processData: false
來禁止該轉換過程。
我們給的
data
就是已經用FormData
編碼好的數據,不需要jQuery進行字符串轉換。
兼容性與其他選擇
本文介紹的jQuery文件上傳方式依賴於FormData
對象, 這是XMLHttpRequest Level 2接口, 需要 IE 10+, Firefox 4.0+, Chrome 7+, Safari 5+, Opera 12+
這意味着對於低版本瀏覽器只能使用直接提交文件表單的形式, 但提交大文件表單頁面會長時間不響應,如果希望在低版本瀏覽器中解決該問題, 就只能使用別的方式來實現了,比如很多支持多文件和上傳進度的Flash插件。
本文采用 知識共享署名 4.0 國際許可協議(CC-BY 4.0)進行許可,轉載注明來源即可: https://harttle.land/2016/07/04/jquery-file-upload.html。學識粗淺寫作倉促,如有錯誤辛苦評論或 郵件 指出。