学废了没 | 前端下载文件的各种姿势

lxf2023-03-09 08:06:01

开启AdminJS成长之旅!这是我参与「AdminJS日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

群友又提了个问题,前端如何下载后端返回的二进制文件,比如下载一个word文件;我寻思着之前不是写过前后端分别实现下载文件的功能吗?怎么还有问题,果然,在仔细了解需求后发现前文《和后端对线 | 前端如何保存base64字符串为文件》并没有说明这种情况,今天就给大家来列举一下浏览器下载文件的各种姿势,如有遗漏欢迎评论区补充

场景

目前想到的是这3个场景

  1. 后端返回base64,或者其他编码格式,前端转码下载
  2. 后端返回二进制,前端直接下载
  3. 后端接口需要填写参数比如,post一些数据,才返回二进制;前端请求接口后下载二进制

1. 后端返回base64,或者其他编码格式,前端转码下载

对于这种情况,在前文中有详细说明,主要步骤如下
对base64(其他)进行解码 -> ASCII码字符串 -> uint8Array -> Blob
两行代码搞定

const data = new Uint8Array(atob(base64).split('').map((v) => v.charCodeAt()))
const blob = new Blob([data])

保存下载就很简单了,用a标签下载即可

const $a = document.createElement('a')
$a.setAttribute('download','a.gif')
$a.href = URL.createObjectURL(blob)
$a.click()

如果是其他编码格式,也是一样的步骤,最终都是转成blob格式下载

2. 后端返回二进制,前端直接下载

这个就更简单了,只要在前端用a标签设置href为下载地址即可,其他内容都由服务端控制

<a href="http://localhost:2345/download">直接下载二进制</a>

如果用js

const $a = document.createElement('a')
$a.href = "http://localhost:2345/download"
$a.click()

3. 后端接口需要填写参数比如,post一些数据,才返回二进制;前端请求接口后下载二进制

这个情况其实和第二种情况差不多,也是后端直接返回二进制文件,只不过接口需要参数校验;这也是今天要补充说明的新场景;同样很简单,结合场景1,2的方法,在请求之后获取到blob文件然后通过a标签下载就可以了

  function downloadFile() {
    fetch('http://localhost:2345/download').then(r => r.blob()).then((res) => {
      const aLink = document.createElement('a')
      aLink.setAttribute('download', 'a.gif')
      aLink.href = URL.createObjectURL(res)
      aLink.click()
    })
  }

那么后端设置的文件名要怎么获取呢?
答案就是在response header获取即可

  function downloadFile() {
    fetch('http://localhost:2345/download').then(async r => {
      const res = await r.blob()
      return {
        res: res,
        fileName: r.headers.get('file-name')
      }
    }).then(({ res, fileName }) => {
      const aLink = document.createElement('a')
      aLink.setAttribute('download', fileName)
      aLink.href = URL.createObjectURL(res)
      aLink.click()
    })
  }

对了,这里有个深坑,如果跨域的话,记得设置一下跨域头,这样才能前端获取到file-name这个header

response.writeHead(200, {
    "Content-Disposition": "attachment; filename=a.gif",
    "Content-Type": "image/gif; ",
    "Access-Control-Allow-Origin": "*",
    "file-name": "test.gif",
    "Access-Control-Expose-Headers": "file-name",
});

源码

所有前后端源码都在这个地址github.com/lulusir/dow… 记得给个star哈

觉得本文有帮助的同学记得给个评论点赞收藏,一键三连ღ( ´・ᴗ・` )


推荐阅读
使用Typescript实现中间件模式,顺便发了个npm包 在线等,后端悄悄改了接口文档被我抓住了怎么办?
和后端对线 | 前端如何保存base64字符串为文件
释放生产力 | Yapi,swagger2,swagger3生成请求代码
什么?在React中也可以使用vue响应式状态管理
clean-js | 在hooks的时代下,使用class管理你的状态
clean-js | 手把手教你写一个羊了个羊麻将版
有没有一种可能,你从来都没有真正理解async