之前分享過一篇拖拽上傳預覽的實現是基於html5的,在那篇文章中,我們實現了拖拽預覽,對於上傳並沒有特別提及到:這篇算作對文件上傳的補充吧
在html5之前我們上傳文件一般都會用form表單進行提交,from.submit()即可
ok,添加需求如下:
1.上傳成功後執行回調函數
2.上傳過程顯示上傳進度條
這兩種需求都不爲過,很多現實中都會需要用到
如果是ajax上傳就可以很簡單的實現兩種效果,而form表單提交則要另想他法,但是傳統的文件上傳還是要基於form表單,
這裏傳統倆字爲啥加粗了,因爲是傳統的,xhr2就開創了非傳統先河,咱們後面會說到。
先說說傳統的做法:
需求一:我們可以和後臺進行配合利用jsonp去完成回調
function formAjax(fn){
var form =$(form),
now = (new Date()).getTime(),
jsonp = 'jsonp' + now,
callback={},
action = form.action;
callback[jsonp] = function(data){
fn && fn(data.statusText);
};
jsonp = 'zip_load.' + jsonp;
if(action.indexOf('callback') > -1){
action = action.replace(/zip_load.jsonp\d+/,jsonp);
}else{
action += (action.indexOf('?') > -1 ? '&callback=' : '?callback=') + jsonp;
}
form.action = action;
form.submit();
}這種方式是利用jsonp方式定義一個函數名通過url傳遞給後臺處理,成功後商議返回同名函數作爲數據返回,前臺再進行回調處理
需求二:我們在form中無法獲得上傳的任何消息,file文件是按照字節流上傳的,不過我們可以利用ajax另外發送請求獲得字節上傳進度信息:
function progress(progress,fn){
var now = (new Date()).getTime();
if(progressUrl.indexOf('pro') > -1){
progressUrl = progressUrl.replace(/pro=\d+/,'pro=' + now);
}else{
progressUrl += (progressUrl.indexOf('?') > -1 ? '&pro=' : '?pro=') + now;
}
$.ajax({
url:progressUrl,//此處url爲專門獲取上傳進度所需接口
type:'get',
dataType:'text',
success:function(data){
data = (new Function("return " + data))();//解析json
var uploadedBytes = data.uploadedBytes,totalBytes = data.totalBytes;
var percentComplete = Math.round(uploadedBytes * 100 / totalBytes),bytesTransfered = '';
if (uploadedBytes > 1024 * 1024)
bytesTransfered = Math.round(uploadedBytes * 100 / (1024 * 1024)) / 100 + 'MB';
else{
bytesTransfered = Math.round(uploadedBytes * 100 / 1024) / 100 + 'KB';
}
fn && fn(percentComplete,bytesTransfered);//進度條處理事件
if(percentComplete !== 100)
setTimeout(function(){
progress(progressUrl,fn);
},200);
}
});
}
在這裏我們將請求的url傳入一個當前日期去無緩存請求最新上傳進度,請求成功之後,後臺會傳回數據,數據根據字節算法處理完成後判斷是否達到100%,
如果未達到則繼續請求,直到上傳完成爲止.
這兩種方式都比較容易理解,無非是添加了一個另外的請求來獲得上傳進度來輔助實現。
我們接下來看看xhr2.0是如何做到的:
html5的xmlhttprequest2.0爲我們帶來了大量新功能例如上傳進度事件以及對二進制數據上傳的支持等,而且不用藉助於form表單,詳細資料參考這裏
介於此,我們可以對支持xhr2.0的瀏覽器利用xhr實現我們的文件上傳以及進度回調等處理:
首先我們利用Window.File去判斷瀏覽器是否支持xhr2.0,如果支持則繼續,
新建一個formData對象(h5特有對象,詳細參見formData介紹)其餘邏輯與上類似。
function uploadAjax(fn,callback){
var file=$('input');
if(window.File && window.FileList && window.FileReader){
if(!file.files.length)
return;
var formData = new FormData(),
xhr = new XMLHttpRequest(),
uploadedBytes = 0,
totalBytes = 0;
formData.append('file',file.files[0]);
xhr.upload.addEventListener("progress", function(evt) {if (evt.lengthComputable) {
uploadedBytes = evt.loaded;totalBytes = evt.total;
var percentComplete = Math.round(uploadedBytes * 100 / totalBytes),
bytesTransfered = '';if (uploadedBytes > 1024 * 1024)
bytesTransfered = Math.round(uploadedBytes * 100 / (1024 * 1024)) / 100 + 'MB';else{
bytesTransfered = Math.round(uploadedBytes * 100 / 1024) / 100 + 'KB';
}
fn && fn(percentComplete,bytesTransfered);if(percentComplete === 100){
//上傳完成
}
}else {
//上傳失敗
}
},false);
xhr.addEventListener('load',function(evt){
callback && callback(evt.target.responseText);},false);
xhr.open('POST',uploadUrl);
xhr.send(formData);
}}
ok,介紹完兩種方式我們來簡單封裝一下:
function callMe(cur,args1,args2){
switch(cur){
case 'progress':
//顯示上傳文件進度條progressBar.style.display = 'block';
progressNumber.style.display = 'block';transferBytesInfo.style.display = 'block';
progressBar.style.width = '0px';
progressNumber.innerHTML = args1 + '%';
progressBar.style.width = args1 * 3.55 + 'px';
transferBytesInfo.innerHTML = args2;
break;
case 'load':
default:
break;}
});}
}
function ajaxUpload(file,fn,progresUrl){
var uploadUrl = file.attr('update_url');if(window.File && window.FileList && window.FileReader){
if(!file.files.length)return;
var formData = new FormData(),xhr = new XMLHttpRequest(),
uploadedBytes = 0,totalBytes = 0;
formData.append('file',file.files[0]);xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {uploadedBytes = evt.loaded;
totalBytes = evt.total;
var percentComplete = Math.round(uploadedBytes * 100 / totalBytes),bytesTransfered = '';
if (uploadedBytes > 1024 * 1024)bytesTransfered = Math.round(uploadedBytes * 100 / (1024 * 1024)) / 100 + 'MB';
else
bytesTransfered = Math.round(uploadedBytes * 100 / 1024) / 100 + 'KB';fn && fn('progress',percentComplete,bytesTransfered);
if(percentComplete === 100){//上傳完成
}
}else {
//上傳失敗
}
},false);
JsUpload.bind(xhr,'load',function(evt){
fn && fn('load',evt.target.responseText);});
xhr.open('POST',uploadUrl);
xhr.send(formData);
}else{
var form =$(form),
now = (new Date()).getTime(),
jsonp = 'jsonp' + now,
callback={},
action = form.action;
callback[jsonp] = function(data){
fn && fn('load',data.statusText);};
jsonp = 'zip_load.' + jsonp;
if(action.indexOf('callback') > -1){
action = action.replace(/zip_load.jsonp\d+/,jsonp);}else{
action += (action.indexOf('?') > -1 ? '&callback=' : '?callback=') + jsonp;}
form.action = action;
form.submit();
//上傳進度setTimeout(function(){
progress(progressUrl,fn);
},200);
}
}
function progress(progressUrl,fn){
if(progressUrl.indexOf('pro') > -1){
progressUrl = progressUrl.replace(/pro=\d+/,'pro=' + now);
}else{
progressUrl += (progressUrl.indexOf('?') > -1 ? '&pro=' : '?pro=') + now;
}
$.ajax({
url:progressUrl,//此處url爲專門獲取上傳進度所需接口
type:'get',
dataType:'text',
success:function(data){
data = (new Function("return " + data))();
var uploadedBytes = data.uploadedBytes,totalBytes = data.totalBytes;
var percentComplete = Math.round(uploadedBytes * 100 / totalBytes),bytesTransfered = '';
if (uploadedBytes > 1024 * 1024)
bytesTransfered = Math.round(uploadedBytes * 100 / (1024 * 1024)) / 100 + 'MB';
else{
bytesTransfered = Math.round(uploadedBytes * 100 / 1024) / 100 + 'KB';
}
fn && fn(percentComplete,bytesTransfered);//進度條處理事件
if(percentComplete !== 100)
setTimeout(function(){
JsUpload.progress(progressUrl,fn);
},200);
}
});
}
調用方法:ajaxUpload($('input'),callMe,'xxxurl');