之前分享过一篇拖拽上传预览的实现是基于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');