最近有这么一个需求,就是后端api地址返回一个文件,这个文件是已经格式化好的,比如表格.xlsx,或者是其他文件zip这些。

但是后端他加了鉴权,所以如果直接a链接访问这个接口,我们无法在头信息上添加token字段,于是这个请求被作为无效。

然后我使用了axios的请求,结果返回的内容,转换为blob对象后,下载完文件就被破坏了,无法打开。

这里我贴一下之前的代码。

api().then(res=>{
  let blob = new Blob([err]);
  let a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = "表格文件.xlsx";
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  URL.revokeObjectURL(a.href); // 释放URL 对象
  a.remove();
}).catch(err=>{
  console.log(err);
})

api是一个封装的axios对象,指定了url地址和自定义头信息,这里就不多说了,主要是文件的下载。

使用该方式,下载的文件无法打开。

后端返回的就是一个表格文件。

没办法,只能使用fetch

fetch("api地址",{
  headers:{
    token: this.$store.getters.token
  },
  method: "POST",
}).then(res=>{
  return res.blob();
}).then(blob=>{
  let a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = "表格文件.xlsx";
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  URL.revokeObjectURL(a.href); // 释放URL 对象
  a.remove();
}).catch(err=>{
  console.log(err);
})

这样就能正确的下载该文件了,打开也无问题。

注意:

有的后端可能无法接收到json格式的数据,可以使用formdata,但是不要讲fetch的协议改为:'Content-Type': 'multipart/form-data'

fetch可以直接传formdata对象,如果你改成这种协议,你会发现上传的参数变得乱七八糟

知识补充

URL.createObjectURL

URL.createObjectURL()方法会生成一个地址,这个地址代表着根据blob对象所生成的资源入口,这个资源入口存放于浏览器的blob URL store 中。

其生成的url地址由四个部分组成:

  1. blob:
  2. origin(域名)
  3. / (斜杠)
  4. UUID

uuid每次都会随机生成,所以即便是下载同一个文件,这个链接地址也是不一样的。

例子:

blob:http://localhost:8080/b59340e5-2182-4607-815d-8ccaedbfe705

由于这个链接用完就不需要再保留,否则会在网页关闭之前一直保留,所以需要手动删除。

URL.revokeObjectURL(a.href);

res.blob

这是fetch的一个功能,在第一个then回调时,返回的是一个响应结果promise对象,如果是文本内容,我们需要使用return出res.json()来让下一个then接收到参数。

如果是文件,我们则使用res.blob()方法,它表示通过字节流读取器读取所有数据,然后包装成blob对象并返回。

然后下一个then就可以接收到已经转换好的blob对象,我们就可以通过URL.createObjectURL创建下载链接了。

每次调用 res.blob() 方法都会执行 “consume body” 动作,“consume body” 的流程大概是这样的:

  1. 获取字节流的读取器
  2. 通过读取器读取所有的数据
  3. 把数据包装成 blob 对象并返回

如果有兴趣可以看看这篇文章,他讲的比我更详细:

谈一谈 Fetch API 中的 “res.blob()”

分类: JavaScript 标签: fetch下载文件blobURL.createObjectURL

评论

暂无评论数据

暂无评论数据

目录