关于处理后端文件流并下载
Mine:
今天实现一个功能的时候遇到了问题:
- 导出二维码到本地并保存为zip文件夹
- 接口:GET
- 接口请求时需要在header携带token
参考了之前的项目的办法(url参数携带token):
- window.open(url,params)后发现无法在header携带token,在新窗口无法获取到正确数据,放弃
因为是GET请求+需要携带token,所以决定先在原窗口发起请求,获取到正确数据后再进行处理并保存文件
- 在封装request的时候已经进行拦截加入token
- 封装api的时候声明格式:responseType: ‘blob’
export const apiName = data => {
return request({
method: 'get',
params:data,
url: '-------url-------',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
responseType: 'blob',
})
}
- 在对应页面调用方法时调用
apiName(params).then( res => {
if(res.status === 200) {
//设置格式
const blob = new Blob([res.data], { type: zip })
const a = document.createElement('a')
a.download = '文件名'
a.href = window.URL.createObjectURL(blob)
a.click()
a.remove()
}
}).catch( err => { console.log(err)})
其他场景:
(前端处理后端文件流并下载)
场景一
Get请求,无需携带token(或在url参数处携带)
//1.直接用标签接收文件流
<a href="后端文件下载接口地址" >下载文件</a>
//2.新建窗口接收文件流
window.open(url)
场景二
Get请求,需要在header处携带token
- 参考上面
场景三
Post请求,利用原生的XMLHttpRequest方法实现
function request () {
const req = new XMLHttpRequest();
req.open('POST', '<接口地址>', true);
req.responseType = 'blob';
req.setRequestHeader('Content-Type', 'application/json');
req.onload = function() {
const data = req.response;
const a = document.createElement('a');
const blob = new Blob([data]);
const blobUrl = window.URL.createObjectURL(blob);
download(blobUrl) ;
};
req.send('<请求参数:json字符串>');
};
function download(blobUrl) {
const a = document.createElement('a');
a.style.display = 'none';
a.download = '<文件名>';
a.href = blobUrl;
a.click();
document.body.removeChild(a);
}
request();
post请求 利用原生的fetch方法实现
function request() {
fetch('url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: 请求参数,
})
.then(res => res.blob())
.then(data => {
let blobUrl = window.URL.createObjectURL(data);
download(blobUrl);
});
}
function download(blobUrl) {
const a = document.createElement('a');
a.style.display = 'none';
a.download = '文件名';
a.href = blobUrl;
a.click();
document.body.removeChild(a);
}
request();
关于Blob
Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
一个Blob对象就是一个包含有只读原始数据的类文件对象。Blob对象中的数据并不一定得是JavaScript中的原生形式。File接口基于Blob,继承了Blob的功能,并且扩展支持了用户计算机上的本地文件。
应用场景:
File接口基于Blob,继承了Blob的功能并进行了扩展,故我们可以像使用Blob一样使用File对象。
- 分片上传:通过Blob.slice方法,可以将大文件分片,轮循向后台提交各文件片段,即可实现文件的分片上传
- 获取要上传文件的File对象,根据chunk(每片大小)对文件进行分片
- 通过post方法轮循上传每片文件,其中url中拼接querystring用于描述当前上传的文件信息;post body中存放本次要上传的二进制数据片段
- 接口每次返回offset,用于执行下次上传
initUpload();
//初始化上传
function initUpload() {
var chunk = 100 * 1024; //每片大小
var input = document.getElementById("file"); //input file
input.onchange = function (e) {
var file = this.files[0];
var query = {};
var chunks = [];
if (!!file) {
var start = 0;
//文件分片
for (var i = 0; i < Math.ceil(file.size / chunk); i++) {
var end = start + chunk;
chunks[i] = file.slice(start , end);
start = end;
}
// 采用post方法上传文件
// url query上拼接以下参数,用于记录上传偏移
// post body中存放本次要上传的二进制数据
query = {
fileSize: file.size,
dataSize: chunk,
nextOffset: 0
}
upload(chunks, query, successPerUpload);
}
}
}
// 执行上传
function upload(chunks, query, cb) {
var queryStr = Object.getOwnPropertyNames(query).map(key => {
return key + "=" + query[key];
}).join("&");
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://xxxx/opload?" + queryStr);
xhr.overrideMimeType("application/octet-stream");
//获取post body中二进制数据
var index = Math.floor(query.nextOffset / query.dataSize);
getFileBinary(chunks[index], function (binary) {
if (xhr.sendAsBinary) {
xhr.sendAsBinary(binary);
} else {
xhr.send(binary);
}
});
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var resp = JSON.parse(xhr.responseText);
// 接口返回nextoffset
// resp = {
// isFinish:false,
// offset:100*1024
// }
if (typeof cb === "function") {
cb.call(this, resp, chunks, query)
}
}
}
}
}
// 每片上传成功后执行
function successPerUpload(resp, chunks, query) {
if (resp.isFinish === true) {
alert("上传成功");
} else {
//未上传完毕
query.offset = resp.offset;
upload(chunks, query, successPerUpload);
}
}
// 获取文件二进制数据
function getFileBinary(file, cb) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (e) {
if (typeof cb === "function") {
cb.call(this, this.result);
}
}
}
- 通过url下载文件
- 通过url显示图片