5

js下载文件的实现方式

 2 years ago
source link: https://segmentfault.com/a/1190000040808084
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

供参考,平时遇到的问题记录

一、普通下载

当后端返回的资源是链接时,此时可以使用 a 标签或者 window.location.href直接打开。

1、a 标签形式

在H5中,a标签新增了download 属性,包含该属性的链接被点击时,浏览器会以下载文件方式下载 href 属性上的链接。

 <a href="https://example.com" download="example.html">下载</a>

// 或者封装
function download(href, title) {
    const a = document.createElement('a');
    a.setAttribute('href', href);
    a.setAttribute('download', title);
    a.click();
}
download('https://example.com', 'test')

2、window.location.href 直接打开

window.location.href === 'https://example.com'

二、流文件下载

当后端返回的文件是流文件时,以 umi-request 请求方法为例

首先应在请求中设置返回类型:responseType: "blob"

import request from "umi-request";

export const downLoad = (url, fileName) =>
  request(url, {
    method: "POST",
    data: data,
    responseType: "blob", // 代表内存之中的一段为二进制
  }).then(data) => {
    const blob = data;
    let link = document.createElement("a");
    link.href = URL.createObjectURL( new Blob([blob], { type: "application/vnd.ms-excel" }) );
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    URL.revokeObjectURL(link.href);
};

1、自定义文件名

此时可以发现后端返回的是流文件,前端接到的是乱码。当前端自定义文件名时,可以直接下载

downLoad('https://example.com/api/download', 'test.xlsx')

2、采用后端定义的文件名

当采用后端的fileName时,此时要获取后端在 content-disposition 中定义的文件名。

首先调用接口,发现文件名是在请求头content-disposition 中,需要注意的是:虽然我们能看到,但是却拿不到请求头。

如果想让浏览器能访问到其他响应头的话,需要后端在服务器上设置 Access-Control-Expose-Headers

// 后端面大致写法
headers.add("Access-Control-Expose-Headers", "Content-Disposition"); 

此时可以拿到相关信息,发现是编码的,需要decodeURI 进行解码

const disposition = response.headers.get('Content-Disposition');

const fileName = decodeURI(disposition.split(";")[1].split("filename=")[1])

注:直接在请求方法里是不能获取到请求头信息的,需要对请求进行拦截

request.interceptors.response.use(async (response) => {
  const disposition = response.headers.get("Content-Disposition"); // 获取Content-Disposition
  return disposition // 当Content-Disposition中有值的时候进行处理,其他请求的响应则放过
    ? {
        blob: await response.blob(), // 将二进制的数据转为blob对象,这一步是异步的因此使用async/await
        fileName: decodeURI(disposition.split(";")[1].split("filename=")[1]), // 处理Content-Disposition,获取header中的文件名
      }
    : response;
});

完整的代码如下:

request 文件

import request from "umi-request";

// 响应拦截
request.interceptors.response.use(async (response) => {
  const disposition = response.headers.get("Content-Disposition"); // 获取Content-Disposition
  return disposition // 当Content-Disposition中有值的时候进行处理,其他请求的响应则放过
    ? {
        blob: await response.blob(), // 将二进制的数据转为blob对象,这一步是异步的因此使用async/await
        fileName: decodeURI(disposition.split(";")[1].split("filename=")[1]), // 处理Content-Disposition,获取header中的文件名
      }
    : response;
});

export const downLoadExcel = (url) =>
  request(url, {
    method: "POST",
    data: data,
    // responseType: "blob", // 注释掉这一段
  }).then(data => {
    const { blob, fileName } = response;
    let link = document.createElement("a");
    link.href = URL.createObjectURL( new Blob([blob], { type: "application/vnd.ms-excel" }) );
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    URL.revokeObjectURL(link.href);
    document.body.removeChild(link);
});

react 文件

<Buttton onClick={download}> 下载 </Button>
async download() {
   await downLoadExcel('http://example.com/api/download');
},

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK