jQuery File Upload是一個(gè)非常優(yōu)秀的上傳組件,主要使用了XHR作為上傳方式,并且利用了相當(dāng)多的現(xiàn)代瀏覽器功能,所以可以實(shí)現(xiàn)諸如批量上傳、超大文件上傳、圖片預(yù)覽、拖拽上傳、上傳進(jìn)度顯示、跨域上傳等功能。
美中不足的是jQuery File Upload的默認(rèn)UI比較復(fù)雜,集成了全部功能,讓jQuery File Upload的定制變得比較繁瑣。
嘗試用jQuery File Upload制作了一個(gè)類似微博圖片上傳的單文件式上傳Demo,將一些要點(diǎn)記錄下來備忘。最終效果如下圖:

jQuery File Upload的最簡(jiǎn)模型
jQuery File Upload包含了一堆文件,首先需要弄清楚的是最核心的部分是哪些,根據(jù)官方的例子可以知道,一個(gè)最簡(jiǎn)單的jQuery File Upload上傳組件,必須包括以下文件:
- jQuery核心庫(kù),建議使用jQuery 1.8以上版本
- js/vendor/jquery.ui.widget.js : jQuery UI Widget
- js/jquery.iframe-transport.js : 擴(kuò)展iframe數(shù)據(jù)傳輸
- js/jquery.fileupload.js : jQuery File Upload核心類
- js/cors/jquery.xdr-transport.js 在IE下應(yīng)載入此文件解決跨域問題
此時(shí)只需要加載一個(gè)上傳按鈕
<input id="fileupload" type="file" name="files[]" data-url="server/php/" multiple>
以及一行代碼
$('#fileupload').fileupload();
就完成了一個(gè)最基本的上傳組件。這個(gè)最簡(jiǎn)單的上傳組件可以將選中的文件以表單形式提交到data-url約定的URL,同時(shí)提供了足夠多的設(shè)置和基礎(chǔ)事件可供擴(kuò)展。
jQuery File Upload的簡(jiǎn)單擴(kuò)展
對(duì)于最簡(jiǎn)模型,稍加擴(kuò)展就可以實(shí)現(xiàn)一些比較常用的功能,比如可以在上傳完畢后可以顯示一個(gè)簡(jiǎn)單的結(jié)果:
$('#fileupload').fileupload({
done: function (e, data) {
$.each(data.result, function (index, file) {
$('<p/>').text(file.name + ' uploaded').appendTo($("body"));
});
}
});
或者顯示上傳進(jìn)度,配合一些進(jìn)度條組件就可以構(gòu)成一個(gè)上傳進(jìn)度條
$('#fileupload').fileupload('option', {
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
console.log(progress + '%');
}
});
等等。只要多閱讀手冊(cè)就可以配合項(xiàng)目做更具體的擴(kuò)展開發(fā)。
XHR響應(yīng)為Json時(shí)IE的下載BUG
這里需要特別注意的是,由于jQuery File Upload都是采用XHR在傳遞數(shù)據(jù),服務(wù)器端返回的通常是JSON格式的響應(yīng),但是IE會(huì)將這些JSON響應(yīng)誤認(rèn)為是文件傳輸,然后直接彈出下載框詢問是否需要下載。
解決這個(gè)問題的方法是必須將相應(yīng)的Http Head從
Content-Type: application/json
更改為
Content-Type: text/plain
具體的實(shí)現(xiàn)根據(jù)服務(wù)端不同有所區(qū)別,比如ZF2中可以在Controller中這樣寫:
$this->getServiceLocator()->get('Application')->getEventManager()->attach(\Zend\Mvc\MvcEvent::EVENT_RENDER, function($event){
$event->getResponse()->getHeaders()->addHeaderLine('Content-Type', 'text/plain');
}, -10000);
這也是我在stackoverflow上的對(duì)ZF2更改最終響應(yīng)類型的一個(gè)回答
jQuery File Upload UI的構(gòu)成與說明
為了引入更多功能,jQuery File Upload在上面最簡(jiǎn)模型的基礎(chǔ)上又實(shí)現(xiàn)了一套jQuery File Upload UI,也就是官方給出的最終Demo,這套UI額外提供了以下功能:
- 最大/最小文件限定 Options.maxFileSize / Options.mixFileSize
- 文件類型限定,通過正則表達(dá)式檢測(cè)文件名實(shí)現(xiàn) Options.acceptFileTypes
- 選擇文件后自動(dòng)上傳 Options.autoUpload
- 上傳文件數(shù)量限制,通過上傳后將選擇文件按鈕置為Disabled實(shí)現(xiàn) Options.maxNumberOfFiles
- 上傳模板,就是選擇文件后顯示預(yù)覽的html代碼 Options.uploadTemplate
- 下載模板,當(dāng)文件上傳完畢后顯示的html代碼 Options.downloadTemplate
等等,同時(shí)還增加了一系列新的接口和事件,具體都可以查閱官方手冊(cè)。
具體對(duì)應(yīng)到文件為:
也許正是因?yàn)楦郊庸δ芴?,各功能之間耦合非常重,jQuery File Upload UI顯得不夠友好,主要體現(xiàn)在:
- 上述功能均無法拆分,必須統(tǒng)一全部加載
- 各功能需要界面存在相應(yīng)元素,如果缺少某些元素,包括JS模板內(nèi)的元素,整個(gè)UI無法正常工作
- JS模板引擎對(duì)標(biāo)簽配對(duì)非常嚴(yán)格,標(biāo)簽如果遺漏也有可能引起UI無法正常工作
所以經(jīng)驗(yàn)之談是,在定制jQuery File Upload UI時(shí),如果UI無法工作。首先檢查js文件是否全部加載,然后檢查頁(yè)面元素是否齊全,再次檢查JS模板標(biāo)簽是否嚴(yán)格配對(duì),最后還可以查看頁(yè)面是否有重復(fù)調(diào)用fileupload()方法。
jQuery File Upload UI構(gòu)成元素
UI的部件都是硬編碼的HTML class,無法更改。核心的幾個(gè)部件為
全局控制按鈕 (必須)
<div class="fileupload-buttonbar">
<span class="fileinput-button"><input type="file" name="files[]" multiple></span>
<button type="submit" class="start">Start upload</button>
<button type="reset" class="cancel">Cancel upload</button>
<button type="button" class="delete">Delete</button>
<input type="checkbox" class="toggle">
</div>
最外層容器為.fileupload-buttonbar,內(nèi)部包含
- 文件選擇按鈕 .fileinput-button (必須),內(nèi)部必須包裹一個(gè)input:file
- 開始上傳按鈕 .start
- 取消上傳按鈕 .cancel
- 刪除按鈕 .delete
- 文件勾選按鈕 .toggle
整體上傳進(jìn)度 (可選)
<div class="fileupload-progress">
<div class="progress">
<div class="bar" style="width:0%;"></div>
</div>
<div class="progress-extended"></div>
</div>
最外層容器為.fileupload-progress,內(nèi)部包含
- 上傳進(jìn)度條容器.progress
- 上傳進(jìn)度條 .bar
- 上傳進(jìn)度文本 .progress-extended
文件顯示容器 (必須)
<div class="files"></div>
.file容器是最重要的UI部件,上傳時(shí)的文件預(yù)覽模板以及上傳完畢后的文件顯示模板都將顯示在這里。
文件預(yù)覽模板 (必須)
<script id="template-upload" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<div class="template-upload">
{% if (file.error) { %}
<div class="error">{%=file.error%}</div>
{% } else { %}
<div class="preview"><span class="fade"></span></div>
<div class="name"><span>{%=file.name%}</span></div>
<div class="size"><span>{%=o.formatFileSize(file.size)%}</span></div>
<div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" style="height:5px;"><div class="bar" style="width:0%;"></div></div>
<span class="start">
{% if (!o.options.autoUpload) { %}
<button>Start Upload</button>
{% } %}
</span>
{% } %}
<span class="cancel"><button>Cancel</button></span>
</div>
{% } %}
</script>
這部分邏輯不難讀懂,由于文件選擇是多選的,所以被選擇文件一開始以數(shù)組方式存放,循環(huán)輸出。即使我們加入最大文件只能上傳一個(gè),這里得到的仍然是數(shù)組形式。
當(dāng)文件有任何錯(cuò)誤時(shí),如文件類型被禁止,文件大小不符合約定,會(huì)得到file.error。文件檢測(cè)沒有問題,則可以用以下元素控制當(dāng)前文件:
- 開始上傳當(dāng)前文件按鈕.start (必須)
- 取消上傳當(dāng)前文件按鈕.cancel (可選)
- 當(dāng)前文件上傳進(jìn)度.progress (可選)
上傳后文件回調(diào)顯示模板 (必須)
<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<div class="template-download">
{% if (file.error) { %}
<div class="error">{%=file.error%}</div>
<span class="cancel"><button class="btn btn-block"><i class="icon-ban-circle"></i>Cancel</span>
{% } else { %}
<div class="preview"><img src="{%=file.thumbnail_url%}"></div>
<div class="name"><span>{%=file.name%}</span></div>
<div class="size"><span>{%=o.formatFileSize(file.size)%}</span></div>
<div class="delete"><button data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">Delete</button>
</div>
{% } %}
</div>
{% } %}
</script>
這一部分的o.files完全來自服務(wù)器端的json響應(yīng),所以模板內(nèi)容可以自由發(fā)揮。唯一被定制的元素為刪除按鈕.delete。 點(diǎn)擊這個(gè)按鈕會(huì)向按鈕中指定的url發(fā)送請(qǐng)求,比如
<div class="delete"><button data-type="DELETE" data-url="/file/1">Delete</button></div>
點(diǎn)擊后則會(huì)用DELETE方式發(fā)送HTTP請(qǐng)求
DELETE /file/1
jQuery File Upload UI工作流程
有了上面羅列的UI元素,就可以拼湊出一個(gè)簡(jiǎn)單的jQuery File Upload UI工作流程:
- 用戶點(diǎn)擊.fileinput-button選擇要上傳的文件(多個(gè))
- 文件選擇后,文件信息被整理為數(shù)組置入文件預(yù)覽模板#template-upload
- 模板引擎循環(huán)處理文件信息并生成模板.template-upload
- 每生成一個(gè)模板,模板就被插入到文件顯示容器.files的最后。
- 用戶點(diǎn)擊上傳按鈕.start上傳,文件信息被轉(zhuǎn)換為XHR請(qǐng)求至服務(wù)器端
- UI獲得服務(wù)器端生成JSON響應(yīng)文件
- JSON響應(yīng)信息也被整理成數(shù)組置入回調(diào)顯示模板#template-download
- 模板引擎循環(huán)處理文件信息并生成模板.template-download
- 每生成一個(gè)模板,會(huì)將此模板替換對(duì)應(yīng)的.template-upload部分
定制過程
有了上面的基礎(chǔ),要個(gè)性化的定制jQuery File Upload就簡(jiǎn)單了很多:
限制文件類型
由于沒有使用Flash空間,上傳的文件選擇框是無法限制文件類型的,所以所謂的限制文件類型,只能讓用戶選擇文件之后,用file.error顯示一個(gè)錯(cuò)誤信息。例如本次需要限定可上傳的文件為圖片,那么Options指定:
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i
即可。
在Google Chrome瀏覽器中,可以用input:file原生支持文件類型限定,可以配合使用:
<input type="file" name="upload[]" accept="image/png, image/gif, image/jpg, image/jpeg">
不過在客戶端做再多的限定也只是提升用戶體驗(yàn),不能真正保證安全性,所以不要忘記了在服務(wù)器端做同樣的類型檢測(cè)。
文件數(shù)量限制
只需在Options指定
maxNumberOfFiles : 1
即可。jQuery File Upload UI的處理方式是當(dāng)用戶上傳一個(gè)文件后,文件選擇按鈕被置為Disabled。
這同樣只是客戶端的小把戲,真正想要嚴(yán)格的約束用戶只能上傳一個(gè)文件還是需要在服務(wù)器端通過Session做更加復(fù)雜的控制。
文件大小限制
Options中指定
maxFileSize: 5000000
即只允許單文件最大5MB。
Firefox disable bug
在Firefox環(huán)境下測(cè)試是,發(fā)現(xiàn)如果將文件數(shù)量限制為1,選擇一次文件,刷新頁(yè)面之后文件選擇按鈕會(huì)莫名其妙的被加上一個(gè)Disabled屬性,導(dǎo)致無法點(diǎn)擊。所以最終我們的初始化代碼為:
var uploader = $("#fileupload");
uploader.fileupload({
dataType: 'json',
autoUpload: false,
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
maxNumberOfFiles : 1,
maxFileSize: 5000000
});
uploader.find("input:file").removeAttr('disabled');
最后就是界面的一些調(diào)整,完整代碼在EvaEngine的File模塊下,點(diǎn)擊查看.
Tags :
jQuery
上傳
jQuery File Upload
插件
Follow :
Donate:Buy me a coffee | 文章有幫助,可以請(qǐng)我喝杯咖啡
|