那么轻,那么重:图片下载与压缩包下载
0. 缘起
下载,不同文本流有不同下载方式。
1. 图片下载
我直接嗯用以前下载图片的方法,改了下文本流的获取和下载名就敢直接用。结果,呃,直接文件损坏无法打开。
//导出图片
exportPic() {
//canvas的toDataURL()方法返回一个包含图片展示的数据URL
const dataURL = this.canvas.toDataURL({
width: this.canvas.width,
height: this.canvas.height,
left: 0,
top: 0,
format: "png",
});
//创建一个a标签,指向图片的URL地址,
//点击即可下载名为canvas.png的图片
const link = document.createElement("a");
link.download = "canvas.png";
link.href = dataURL;
//当前文档的Body对象上挂载一个元素,此处为a标签
//模拟a标签的点击,下载后移除该元素
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
2. 压缩包下载的正确操作
2.1 Axios
用的axios,就从它的封装方法里面看。因为涉及到response有没有被更改
const instance = axios.create({
baseURL,
timeout: requestTimeout,
headers: {
"Content-Type": contentType,
}
});
instance.interceptors.response.use(
(response) => {
if (loadingInstance) loadingInstance.close();
const { data, config } = response;
console.log('response: ', response);
const { code, message } = data;
let successFlag = response.status;
// 操作正常Code数组
const codeVerificationArray = isArray(successCode)
? [...successCode]
: [...[successCode]];
// 是否操作正常 Old Version
// if (codeVerificationArray.includes(code)) {
// New Version ***
if (successFlag === 200) {
// Special Blob Type
if (response.config.responseType === 'blob') {
return response
} else {
return data;
}
} else {
console.log(code, message)
handleCode(code, message);
return Promise.reject(
"请求异常拦截:" +
JSON.stringify({ url: config.url, code, message }) || "Error"
);
}
},
(error) => {
if (loadingInstance) loadingInstance.close();
const { response, message } = error;
if (error.response && error.response.data) {
const { status, data } = response;
handleCode(status, data.message || message);
return Promise.reject(error);
} else {
let { message } = error;
if (message === "Network Error") {
message = "后端接口连接异常";
}
if (message.includes("timeout")) {
message = "后端接口请求超时";
}
if (message.includes("Request failed with status code")) {
const code = message.substr(message.length - 3);
message = "后端接口" + code + "异常";
}
Vue.prototype.$baseMessage(message || `后端接口未知异常`, "error");
return Promise.reject(error);
}
}
);
2.2 Response
注意当成功的时候,之前是直接返回了response里面的data,而我们在获取文件流,要注意responseType得是blob
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // 默认的
比如这里,我的下载压缩包方法
// Download MagicBoxVersion
export function downloadMagicBoxEdition(id) {
return request({
url: `/.../export?id=${id}`,
method: 'POST',
baseURL: baseURLB,
responseType: 'blob',
})
}
2.3 Data
到这一步在下载按钮的download函数里,传个值过去就能获得对应的文件流了。接下来就是如何下载:
handleExport(row) {
downloadMagicBoxEdition(row.id).then((res) => {
console.log(res);
let blob = new Blob([res.data], {
type: "application/octet-stream;charset=utf-8",
}); // res.data就是接口返回的文件流了
let name = res.headers["content-disposition"];
let fileName = `${decodeURIComponent(
name.split(";")[1].split("=")[1]
)}`.replace("zip", ".zip");
if ("download" in document.createElement("a")) {
// 非IE下载
const elink = document.createElement("a");
elink.download = fileName;
elink.style.display = "none";
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
} else {
// IE10+下载
navigator.msSaveBlob(blob, fileName);
}
});
},
2.4 Title
上面有个fileName
,文件名,后端用content-disposition
传了过来。
filename
后面是要传送的文件的初始名称的字符串。这个参数总是可选的,而且不能盲目使用:路径信息必须舍掉,同时要进行一定的转换以符合服务器文件系统规则。这个参数主要用来提供展示性信息。当与
Content-Disposition: attachment
一同使用的时候,它被用作"保存为"对话框中呈现给用户的默认文件名。
let name = res.headers["content-disposition"];
let fileName = `${decodeURIComponent(
name.split(";")[1].split("=")[1]
)}`.replace("zip", ".zip");
download函数里面的这段,显然是从头里面取出传递过来的文件名。
PS: 这里一定要注意第一步axios里面封装axios所传递的response是否是完全体,一般都传的response.data,是无法获取到header部分的!
3. 函数化的下载方法
就是1个下载来res,另一个处理res
/**
* 下载文件
* @param {*} url: 请求地址
* @param {*} params: 请求参数
*/
export function downFile(url, params) {
return new Promise((resolve, reject) => {
axios({
method: "get",
url: url,
params: params,
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
responseType: 'blob',
baseURL: BaseURL
})
.then((res) => {
resolve(res)
})
.catch((err) => {
reject("错误信息:", err);
});
})
}
// 下载文件
download(res,fileName) {
console.log(res);
let blob = new Blob([res.data], {
type: "application/octet-stream;charset=utf-8",
}); // res.data就是接口返回的文件流了
if ("download" in document.createElement("a")) {
// 非IE下载
const elink = document.createElement("a");
elink.download = fileName;
elink.style.display = "none";
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
} else {
// IE10+下载
navigator.msSaveBlob(blob, fileName);
}
},
使用
this.downFile("/export", {
endDate: max,
startDate: min,
})
.then((res) => {
this.download(res, fileName);
})
.catch((req) => {});
直接发文件下载链接,还是用Post方法
fileName = "产能数据.xlsx",
res = await exportProductData(deliver, fileName);
this.download(res, fileName);
},
// 下载文件
download(res, fileName) {
var a = document.createElement("a");
var url = res.data;
var filename = fileName;
a.href = url;
a.download = filename;
a.click();
},
附录
先前写过的上传方法
众里寻他千百度,蓦然回首,那人却在灯火阑珊处:上传の方法 - 乐盘游 - 博客园 (cnblogs.com)
关于文件上传的参考文章,不过直接用el-upload也行
人生到处知何似,应似飞鸿踏雪泥。