导出功能如何从响应头里面获取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 里面列出来。

### HTTP请求头与响应头在Excel文件导出中的设置 #### 请求头配置 当通过前端向后端发起Excel文件导出请求时,通常会使用`axios`或其他类似的HTTP客户端库。为了正确接收二进制数据并将其保存为Excel文件,需对请求头进行如下配置: - **指定响应类型** 需要将`responseType`设置为`blob`,以便能够处理服务器返回的二进制数据流[^2]。 ```javascript export function exportStorage(params) { return request({ url: '/agcloud/zhps/storage/export', method: 'get', responseType: 'blob', // 设置响应类型为 blob params, headers: { Authorization: `Bearer ${token}`, // 如果需要认证,则在此处添加 token 'Content-Type': 'application/json' // 可选,默认情况下可能不需要显式声明 } }); } ``` 上述代码片段展示了如何在请求中设置必要的参数以及自定义请求头字段,例如用于身份验证的`Authorization`令牌。 --- #### 响应头配置 在服务端完成Excel文件生成后,需要通过HTTP响应头告知浏览器该资源是一个可下载的附件,并提供合适的文件名编码方式以防止乱码问题发生。以下是常见的响应头设置及其作用说明: 1. **Content-Disposition** - 定义了内容显示的方式(内联还是作为附件),并通过附加属性指定了默认文件名称。 - 示例:`Content-Disposition: attachment; filename="example.xlsx"` 表明这是一个供用户下载的附件文件[^3]。 2. **Content-Type** - 描述实际传输的数据格式,在这里应该设为适合Excel文档的标准MIME类型之一: - 对于`.xls` 文件——`application/vnd.ms-excel` - 对于 `.xlsx` 文件——`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`[^4] 3. **其他推荐选项** - 当涉及跨域资源共享(CORS)场景下,还需注意适当调整Access-Control-Allow-*系列头部项来允许特定源访问API接口[^5]。 下面给出一段典型的Spring Boot风格的服务端实现例子,演示这些概念的应用过程: ```java @RequestMapping(value="/download", produces={"application/vnd.ms-excel"}) public ResponseEntity<Resource> downloadFile(@RequestParam String fileName){ InputStreamResource resource = null; try{ byte[] contentBytes = Files.readAllBytes(Paths.get("/path/to/" + fileName)); HttpHeaders header = new HttpHeaders(); // 设定 Content-Disposition 头部信息 header.set(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename="+URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString())); // 返回带有合适头部及状态码的对象给调用方 return ResponseEntity.ok() .headers(header) .contentLength(contentBytes.length) .contentType(MediaType.parseMediaType("application/vnd.ms-excel")) .body(new ByteArrayResource(contentBytes)); }catch(Exception e){ throw new RuntimeException(e); } } ``` 此段代码实现了基本的功能需求,即读取本地磁盘上的某个预置好的Excel模板文件并向外部暴露可供直接获取链接地址的方法。 --- ### 注意事项 除了以上提到的技术细节之外,还有一些额外需要注意的地方包括但不限于安全性考量如输入校验过滤非法字符注入风险;性能优化方面则要考虑大规模并发情况下的内存占用控制策略等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值