ajax和Fetch Api具备基本相同的功能,但是FetchApi的可扩展性更强大,特别是支持Http Header的自由控制,从而可以实现一些ajax不能实现的功能,比如以下代码就是利用fetch自定义了一个分块下载协议,实现数据的分块下载以及断点续传。当然此示例仅仅是供学习研究,并不具备可用性(fetch只有部分浏览器支持+url长度的限制下载不了大文件)
;(function(window, $){
function H5Downloader(aBtn){
if ( !fetch ) throw 'Fetch接口未定义!';
this.aBtn = aBtn || document.createElement('a');
this.auto = !aBtn;
this.filename = "";
this.totalRange = 0;
this.curRange = 0;
this.rangeSpan = 3;
this.dataBuffer = [];
this.unsortData = [];
this.lockFlag = false;
this.lockTimer = 0;
this.downloadUrl = '';
this.sessionToken = '';
this.unit = 'rows';
this.pause = false;
this.mode = 'common';
}
H5Downloader.prototype.onbegin = H5Downloader.prototype.ondata = H5Downloader.prototype.onend = function(){
return this;
}
H5Downloader.prototype.showMsg = function(msg){
alert(msg);
}
H5Downloader.prototype.lock = function(expired){
expired = expired || 200; //默认200ms超时
if(this.lockFlag) return false;
this.lockFlag = true;
this.lockTimer = setTimeout((function(that){
return function(){that.unlock()};
})(this), expired);
return true;
}
H5Downloader.prototype.unlock = function(){
this.lockFlag = false;
clearTimeout(this.lockTimer);
return true;
}
H5Downloader.prototype.serverError = function(response){
var that = this;
response.text().then(function(msg){
console.log(msg);
that.showMsg('网络异常,下载中断');
});
}
H5Downloader.prototype.networkError = function(error){
console.log('There has been a problem with your fetch operation: ' + error.message);
this.showMsg('网络异常,下载失败!');
}
H5Downloader.prototype.stop = function(callback){
this.pause = true;
typeof(callback)=='function' && callback();
}
H5Downloader.prototype.restart = function(){
this.pause = false;
this.getFileData(this.curRange);
}
H5Downloader.prototype.requestDownload = function(downloadUrl){
if(!downloadUrl) throw 'Download Url required!';
this.downloadUrl = downloadUrl;
var that=this;
var myHeaders = new Headers();
myHeaders.append("X-BBC-Askfor", "download = init");
myHeaders.append("X-BBC-Range", that.unit+"=*");
var myInit = {headers:myHeaders};
fetch(this.downloadUrl, myInit).then(function(response) {
if(response.ok) {
var range = RegExp(that.unit+'\s*?\=\s*?(.+?)$').exec(response.headers.get('X-BBC-Range'))[1].replace(/^\s+?|\s+?$/)*1;///rows\s*?\=\s*?(.+?)$/
that.totalRange = range;
that.filename = that.getFileName(response);
that.sessionToken = response.headers.get('X-BBC-SessID');
that.onbegin();
if(that.mode=='thread' && window.Worker)
that.downloadByThread();
else{
that.getFileData(0);
}
} else {
that.serverError(response);
}
}).catch(function(error) {
that.networkError(error);
});
}
H5Downloader.prototype.getFileData = function(startRange){
var that=this;
var myHeaders = new Headers();
//myHeaders.append("Range", "bytes=5-7");
myHeaders.append("X-BBC-Askfor", "download = transfer");
myHeaders.append("X-BBC-Range", that.unit+"="+startRange+"-"+(startRange+that.rangeSpan));
var myInit = {headers:myHeaders};
fetch(this.downloadUrl, myInit).then(function(response) {
if(response.ok) {
if(that.curRange >= that.totalRange){
var myBlob = new Blob(that.dataBuffer);
that.onend();
that.attach(myBlob, that.filename);
}else{
response.blob().then(function(myBlob) {
that.dataBuffer.push(myBlob);
that.curRange = startRange+that.rangeSpan;
if(that.curRange >= that.totalRange){
var myBlob = new Blob(that.dataBuffer);
that.onend();
that.attach(myBlob, that.filename);
return;
}
that.ondata(myBlob);
!that.pause && that.getFileData(that.curRange);
});
}
} else {
that.serverError(response);
}
}).catch(function(error) {
that.networkError(error);
});
}
H5Downloader.prototype.serializeProperties = function(){
var that = this;
return {
downloadUrl: that.downloadUrl,
totalRange: that.totalRange,
rangeSpan: that.rangeSpan,
sessionToken: that.sessionToken,
unit: that.unit
}
}
H5Downloader.prototype.buildTaskData = function (){
var num = Math.ceil(this.totalRange/this.rangeSpan), startList=[], start=0, attachData = this.serializeProperties();
for(var i=0; i<num; i++){
startList.push({startRange: start, attachData: attachData});
start += this.rangeSpan;
}
return startList;
}
H5Downloader.prototype.downloadByThread = function(){
var taskData = this.buildTaskData(), that=this;
//console.log(taskData);
var tdmgr = new ThreadManager(taskData.length, 'worker.js', taskData, function (data){
//处理成功消息
var lock = that.lock();
if(false===lock) throw '下载的数据出现错误';
that.unsortData.push([data.startRange, data.blob]);
that.unlock();
that.ondata(data.blob);
}, function(error){
//处理错误消息
console.log(error);
}, function (){
//下载完成后对数据进行排序组装
var unsortData = that.unsortData;
if(unsortData.length != taskData.length){
that.showMsg('下载过程中出现错误,请重新下载!');
return;
}
//排序
unsortData.sort(function(x, y){
return x[0] -y[0];
});
for(var i=0,t=that.unsortData.length; i<t; i++){
that.dataBuffer.push(unsortData[i][1]);
}
var myBlob = new Blob(that.dataBuffer);
that.onend();
that.attach(myBlob, that.filename);
//console.log(unsortData);
console.log('Finished');
});
}
H5Downloader.prototype.getFileName = function(response){
return response.headers.get('X-BBC-FileName') || (function(){
var find = /filename\=\s*?(.+?)$/.exec(response.headers.get('Content-Disposition')); //解析"attachment;filename=my_test.csv"
return find[1]?find[1].replace(/^\s+?|\s+?$/) : null;
})() || ((new Date()).getTime() + ".unknow");
}
H5Downloader.prototype.buildBlob = function (dataArr){
return new window.Blob(dataArr); //dataArr是一个包含数据的一维数组
}
H5Downloader.prototype.attach = function (blob, filename){
var url = window.URL.createObjectURL(blob);
this.aBtn.href = url;
//this.aBtn.textContent = decodeURIComponent(filename);
//this.aBtn.title = txt;
this.aBtn.download = decodeURIComponent(filename);
this.auto && this.aBtn.click();
return this;
}
H5Downloader.prototype.download = function(url,options){
options = options || {};
this.rangeSpan = options['rangeSpan'] || 3;
this.unit = options['rangeUnit'] || 'bytes';
this.mode = options['mode'] || 'common';
this.requestDownload(url);
}
window.H5Downloader = H5Downloader;
return H5Downloader;
})(window, $);
$(function(){
var d = new H5Downloader(document.getElementById('download_btn'));//
$('#stop_btn').click(function(){
d.stop();
$(this).hide();
$('#restart_btn').show();
});
$('#restart_btn').click(function(){
d.restart();
$(this).hide();
$('#stop_btn').show();
});
d.onbegin = function (){
console.log('Start Time:'+(new Date()).getTime());
}
d.onend = function(){
var txt = '可以下载了';
this.aBtn.textContent = txt;
this.aBtn.title = txt;
this.aBtn.onclick = null;
console.log('End Time:'+(new Date()).getTime());
}
d.ondata = function(){
console.log('finished:'+(this.curRange/this.totalRange*100).toFixed(2)+'%');
}
//d.download('Easy/Test/testDownload?filename=download.txt', {'rangeUnit':'rows','rangeSpan':3, mode:'threadd'});
//d.download('Easy/Design/blockdownload?formcode=5a1b741c8ddcf', {'rangeUnit':'rows','rangeSpan':4});
//d.download('Easy/Design/blockdownload?formcode=59844c9204fea', {'rangeUnit':'rows','rangeSpan':100, mode:'thread'});
});
下载按钮的点击事件:
downloadBtn.onclick = function(){
fetch('easy/design/requestExport?formcode=59844c9204fea&action=create').then(function(response) {
response.text().then(function(txt){
console.log(txt);
})
}).catch(function(error) {
console.log(error);
});
}