Bootstrap

vue 下载文件时,设置返回值类型responseType为‘blob‘无效的问题

最近在做一个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,可以正常下载了。困扰了我一下午啊,终于解决了。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;