导出功能如何从响应头里面获取fileName文件名

问题:导出功能,无法获取对应文件名。

        一般是从响应头的content-disposition字段获取对应的fileName。

        贴上前端代码:

/**
 * 下载模板
 */
function handleDownload() {
  downloadExcel()
    .then(res => {
      let fileName  //文件名
      if (res && res.headers && res.headers['content-disposition']) {
        fileName = decodeURIComponent(res.headers['content-disposition'].split(';')[1].split('filename=')[1])
      } else {
        fileName = `导入用户模板`
      }
      getExcelData(res.data, fileName)
    })
    .catch(err => {
      proxy.$errorHandle(err)
    })
}

/**
 * 下载excel表格
 * @param data file文件
 * @param fileName 表格名
 */
export function getExcelData(data, fileName) {
  const link = document.createElement('a')
  const blob = new Blob([data], { type: 'application/vnd.ms-excel' }) //.xls文件
  link.style.display = 'none'
  const url = window.URL.createObjectURL(blob)
  link.href = url
  link.setAttribute('download', fileName)
  document.body.appendChild(link)
  link.click()
  window.URL.revokeObjectURL(url)
  document.body.removeChild(link)
}

        要成功拿到res.headers['content-disposition'],需要注意以下几点,都是踩过的坑。

1. 响应拦截器是否拦截了res.headers

        检查下项目是怎么封装http请求的,有时候响应拦截器会把res.headers过滤掉,直接返回res.data,这时候我们接收到的响应当然就没有请求头了,需要从响应拦截器处修改。

        贴一个能正常返回returnCode、data和headers的代码,具体还得看你项目怎么封装的:

 // 响应拦截器
  instance.interceptors.response.use(
    // 默认的拦截器
    function (response: AxiosResponse<any, any>): AxiosResponse<any, any> | Promise<AxiosResponse<any, any>> {
      // 包含了code>=200和code<400的请求(但是业务系统会自己定义一些业务code)returnCode=000000也表示成功
      if (response?.data && Object.prototype.hasOwnProperty.call(response.data, 'returnCode')) {
        if (response.data.returnCode === ResponseStatus.SUCCESS) {
          return response.data
        } else {
          return Promise.reject({
            code: response?.data?.returnCode,
            message: response?.data?.message ?? '业务功能异常!'
          })
        }
      } else {
        // 处理返回流
        if (response.data && Object.prototype.toString.call(response.data) === '[object Blob]') {
          return {
            // 重新组装响应
            returnCode: parseInt(ResponseStatus.SUCCESS),
            data: response.data,
            headers: response.headers
          } as any
        }
        throw new Error(`${response.config.url}接口返回数据格式错误`)
      }
    },
    function (error) {
      // TODO: 404和没有权限处理
      // 401未授权
      // 404请求地址不存在
      // 500 网络错误
      if (error.response && String(error.response.code).startsWith('5')) {
        return Promise.reject({ code: '500', message: '网络异常,请重试' })
        //路由切换异常的处理
      } else if (error.message === 'Request Cancel') {
        return Promise.reject({ code: ResponseStatus.REQUEST_CANCEL, message: error.message })
      }
      return Promise.reject(error)
    }
  )

2. 检查对应接口的请求头是否包含Content-Disposition

        确保后端已经设置并且Content-Disposition字段

response.setHeader("Content-Disposition", ...)

3. 是否设置了请求头暴露对应的Content-Disposition

        十分重要,不知道为啥对接的俩后端都没设置过暴露这个参数,我怎么拿都拿不到。

        解决办法:让你的后端加上这行!

// 暴露Content-Disposition
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition")

        解释下为什么要加这个

原因:
        MDN文档:Access-Control-Expose-Headers
        默认情况下,header 只有7种 simple response headers (简单响应首部)可以暴露给外部:

名称
Cache-Control通用消息头字段,被用于在http请求和响应中,通过指定指令来实现缓存机制
Content-Language说明访问者希望采用的语言或语言组合,这样的话用户就可以根据自己偏好的语言来定制不同的内容
Content-Length指明发送给接收方的消息主体的大小,即用十进制数字表示的八位元组的数目
Content-Type告诉客户端实际返回的内容的内容类型
Expires响应头包含日期/时间, 即在此时候之后,响应过期
Last-Modified包含源头服务器认定的资源做出修改的日期及时间
PragmaHTTP/1.0 中规定的通用首部,用来向后兼容只支持 HTTP/1.0 协议的缓存服务器

        content-disposition 不在headers默认暴露的范围内,即使服务器在响应头里加了该字段,但因没”暴露“给外部,客户端就“看得到,吃不到”。

        响应首部 Access-Control-Expose-Headers 就是控制“暴露”的开关,它列出了哪些首部可以作为响应的一部分暴露给外部。

        所以如果想要让客户端可以访问到其他的首部信息,服务器不仅要在 header 里加入该首部,还要将它们在 Access-Control-Expose-Headers 里面列出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值