需求分析: 因为axios在请求时定制了很多个性化需求,比如携带token,比如指明服务器域名等等,如果采用相对地址href的请求方式以及部分框架自带的文件上传方式,是无法满足需求的,我们的目的是采用相同的get和post完成文件下载和上传工作
下面直接分享解决方案
//文件上传, 相对简单些,返回的都是json数据
submitUpload() {
if(!this.checkUpload()) return false
let fileFormData = new FormData();
//this.file 更具不同ui框架获取的file对象,this.file.raw文件的二进制流
fileFormData.append('file', this.file.raw);
let requestConfig = {
//重新设置请求头,考虑服务器处理时间可能过长,加大timeout
headers: {
'Content-Type': 'multipart/form-data',
timeout: 20000,
},
}
this.$axios.post(this.url, fileFormData, requestConfig).then(() => {
this.$message({
message: '文件上传成功',
type: 'success',
duration: 1500,
onClose: () => {
//文件上传已经做成了组件.vue,向父类组件发送done消息,用于更新table等
this.$emit('done')
}
})
})
},
//文件下载
//重点:一切正常,返回的是blob数据,创建一个a标签下载文件
//难点:如果服务器报,返回的可是一个json数据,特殊处理提取出来
download(){
this.$axios.get(this.url, {
responseType: 'blob', // important
timeout: 20000,
}).then((response) => {
//返回的是一个错误
if(response.headers['content-type']==='application/json'){
//提取错误的json消息并转换(此时json消息依旧在response.data中,这是一个blob数据)
let reader = new FileReader();
reader.onload = e => this.$message.error(JSON.parse(e.target.result).msg);
reader.readAsText(response.data);
return false;
}
//接收的是文件,fileName是一个property,设置了默认值(比如"文件模版.xls")
let fileName = this.fileName;
//服务器返回的请求头中包含了文件名信息,提取出来
//可能出现的BUG:如果在响应头中能看到"content-disposition",axios拿到的response却没有,
//这不怪axios,你需要去服务器设置一个叫Access-Control-Expose-Headers的东西
//让它包含"content-disposition"
if(response.headers["content-disposition"]){
const patt = new RegExp("filename=([^;]+\\.[^\\.;]+);*");
const result = patt.exec(response.headers["content-disposition"]);
if(result.length>1){
//中文解码
fileName = decodeURI(result[1]);
}
}
//制作a标签并点击下载
const url = window.URL.createObjectURL(new Blob([response.data],
{ type: 'application/octet-stream' }));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
});
总结: 文件上传在于获得文件二进制流并制作成FormData进行post请求,同时header需设置Content-Type='multipart/form-data'。
而文件下载则更复杂些,需从response.data中获得blob(文件二进制流)创建本地的下载标签a,最后点击下载,同时还需注意如果发生错误,服务器返回的是json数据,该数据依旧存在于response.data并以blob形式存在,需特殊处理提取出来并解析。
以上,依照不重复造轮子原则,可以把这两类分别做成两个组件,合理利用<slot>标签和property属性做到代码写一次,到处可复用原则。