最近在做一个vue项目,需要下载后端传输的pdf文件流。开发完成之后,测试时,发现文件可以下载下来,但是打开是空白的。
1.调用接口。这个service是我简单把axios封装了一下。也可以直接用原生的。
// 下载文件
export function downloadFile(params) {
const config = {
responseType: 'blob',
}
const fetchUrl = `/file/download`
return service.post(fetchUrl, { fileId: params }, config)
}
重点是: responseType:'blob' 这个是设置请求返回类型的
import axios from 'axios'
const baseAddress = 'http://127.0.0.1:31943'
const service = axios.create({
baseURL: baseAddress,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
'Access-Control-Allow-Origin': '*'
},
responseType: 'json'
})
// 请求拦截
service.interceptors.request.use( config => {
const token = localStorage.getItem('loginInfo')
config.headers.token = token
return config
}, error => {
const { response } = error
if (response) {
const { status } = response
router.push({
path: `/error/${status}`
})
}
return Promise.reject(error)
})
// 请求响应
service.interceptors.response.use(
response => {
if (response.status === 200) {
if (response.data instanceof Blob) {
return Promise.resolve(response)
}
return Promise.resolve(response.data)
} else {
return Promise.reject(response)
}
},
err => {
return Promise.reject(err)
}
)
export default service
2.调用接口,拿到返回值,模拟超链接点击下载文件
handleFileClick(rows) {
let { fileId, fileName } = rows
downloadFile(fileId).then(res => {
let blob = new Blob([res.data], { type: 'application/pdf;charset-UTF-8' });
let link = document.createElement("a");
link.style.display = 'none'
link.href = URL.createObjectURL(blob);
link.download = decodeURIComponent(fileName);
document.body.appendChild(link);
link.click();
}).catch().finally(() => {
})
},
3.后端
输出文件流到前台。测试截图如下。
F12 打开浏览器一看,浏览器拿到的不是Blob类型文件,而是一串字符串乱码
本来一开始还觉得是后端的问题,结果postman测试接口发现可以正常下来。于是一顿搜索,终于在网上找到了了解决方法。原来是MockJS搞定的鬼。
原因在于:
“mock模块会影响原生的ajax请求,使得服务器返回的blob类型变成乱码”
我这个项目中使用了mockjs来模拟请求后端接口获得数据,虽然代码中,mock并没有对这个下载接口做拦截,但是这个接口返回blob有问题了。
于是我看了下mockjs的源码,大概知道原因在哪了。
// 初始化 Response 相关的属性和方法
Util.extend(MockXMLHttpRequest.prototype, {
responseURL: '',
status: MockXMLHttpRequest.UNSENT,
statusText: '',
// https://xhr.spec.whatwg.org/#the-getresponseheader()-method
getResponseHeader: function(name) {
// 原生 XHR
if (!this.match) {
return this.custom.xhr.getResponseHeader(name)
}
// 拦截 XHR
return this.custom.responseHeaders[name.toLowerCase()]
},
// https://xhr.spec.whatwg.org/#the-getallresponseheaders()-method
// http://www.utf8-chartable.de/
getAllResponseHeaders: function() {
// 原生 XHR
if (!this.match) {
return this.custom.xhr.getAllResponseHeaders()
}
// 拦截 XHR
var responseHeaders = this.custom.responseHeaders
var headers = ''
for (var h in responseHeaders) {
if (!responseHeaders.hasOwnProperty(h)) continue
headers += h + ': ' + responseHeaders[h] + '\r\n'
}
return headers
},
overrideMimeType: function( /*mime*/ ) {},
responseType: '', // '', 'text', 'arraybuffer', 'blob', 'document', 'json'
response: null,
responseText: '',
responseXML: null
})
mockjs初始化的时候给拦截响应设置了 responseType: ''
那就先把mockjs给注释掉。
找到 下面这句引入mock的代码,加个注释
require('@/mock')
重启项目。再测试一下。
大功告成,返回的变成了blob,可以正常下载了。困扰了我一下午啊,终于解决了。