在Web开发中,前端下载文件是一个常见的需要。依据文件起源和下载方式的不同,前端下载文件能够采纳多种计划。上面将介绍几种常见的前端下载文件计划。

1、通过 window.open() 关上新页面下载文件

window.open(`url`, "_self");

应用场景:下载 excel 文件,后端提供接口,接口返回的是文件流,能够间接应用 window.open(),最简略的形式。

  • 长处:最简洁;
  • 弊病:当参数谬误时,或其它起因导致接口申请失败,这时无奈监听到接口返回的错误信息,须要保障申请必须是正确的且能正确返回数据流,不然关上页面会间接输入接口返回的错误信息,体验不好。

2、通过 a 标签关上新页面下载文件

export const exportFile = (url, fileName) => {  const link = document.createElement("a");  const body = document.querySelector("body");  link.href = url;  link.download = fileName;  // fix Firefox  link.style.display = "none";  body.appendChild(link);  link.click();  body.removeChild(link);};

通过 a 标签下载的形式,同 window.open()是一样的,惟一的长处是能够自定义下载后的文件名,a 标签里有 download 属性能够自定义文件名。

弊病:同 window.open()形式一样,无奈监听错误信息。

问题:以上两种形式,当在下载.mp3 格局,或者视频文件时,浏览器会间接播放该文件,而达不到间接下载的性能,此时,当下载音视频文件时无奈应用以上两种形式。

3、通过文件流的形式下载

为了解决.mp3 文件下载所带来的问题,通过 ajax 申请返回 Blob 对象,或者 ArrayBuffer 对象。

获取文件

如下:通过原生 ajax 申请返回 Blob 对象

const getBlob = (url) => {  return new Promise((resolve) => {    const xhr = new XMLHttpRequest();    xhr.open("GET", url, true);    xhr.responseType = "blob";    xhr.onload = () => {      if (xhr.status === 200) {        resolve(xhr.response);      }    };    xhr.send();  });};

同样,也能够通过 axios 返回 ArrayBuffer 对象来解决

import axios from "axios";const getFile = (url) => {  return new Promise((resolve, reject) => {    axios({      method: "get",      url,      responseType: "arraybuffer",    })      .then((data) => {        resolve(data.data);      })      .catch((error) => {        reject(error.toString());      });  });};

ArrayBuffer(又称类型化数组)

ArrayBuffer 对象用来示意通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 不能间接操作,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格局,并通过这些格局来读写缓冲区的内容。 Blob(Binary Large Object): 二进制大数据对象

Blob 对象示意一个不可变、原始数据的类文件对象。Blob 示意的不肯定是 JavaScript 原生格局的数据。File 接口基于 Blob,继承了 blob 的性能并将其扩大使其反对用户零碎上的文件。 留神:

如果下载文件是文本类型的(如: .txt, .js 之类的), 那么用 responseType: 'text'也能够, 然而如果下载的文件是图片, 视频之类的, 就得用 arraybuffer 或 blob,更多详情请查看 MDN 通过 ajax 申请的形式下载文件,能够解决第 1、2 中存在的弊病,当申请谬误时或捕捉到错误信息

保留文件

当获取到文件后,这时须要保留文件

const saveAs = (blob, filename) => {  if (window.navigator.msSaveOrOpenBlob) {    navigator.msSaveBlob(blob, filename);  } else {    const link = document.createElement("a");    const body = document.querySelector("body");    link.href = window.URL.createObjectURL(blob); // 创建对象url    link.download = filename;    // fix Firefox    link.style.display = "none";    body.appendChild(link);    link.click();    body.removeChild(link);    window.URL.revokeObjectURL(link.href); // 通过调用 URL.createObjectURL() 创立的 URL 对象  }};

为了解决 IE(ie10 - 11)和 Edge 无奈关上 Blob URL 链接的办法,微软本人有一套办法 window.navigator.msSaveOrOpenBlob(blob, filename),关上并保留文件,以上代码做了简略的兼容,navigator.msSaveBlob(blob, filename)是间接保留。留神,此为非标准性能,详情请查看相干文档。

以下为残缺代码(原生版)

const getBlob = ({ method = "GET", url }) => {  return new Promise((resolve) => {    const xhr = new XMLHttpRequest();    xhr.open(method, url, true);    xhr.responseType = "blob";    xhr.onload = () => {      if (xhr.status === 200) {        resolve(xhr.response);      }    };    xhr.send();  });};const saveAs = (blob, filename) => {  if (window.navigator.msSaveOrOpenBlob) {    navigator.msSaveBlob(blob, filename);  } else {    const link = document.createElement("a");    const body = document.querySelector("body");    link.href = window.URL.createObjectURL(blob); // 创建对象url    link.download = filename;    // fix Firefox    link.style.display = "none";    body.appendChild(link);    link.click();    body.removeChild(link);    window.URL.revokeObjectURL(link.href); // 通过调用 URL.createObjectURL() 创立的 URL 对象  }};export const download = (url, filename = "") => {  getBlob({ url }).then((blob) => {    saveAs(blob, filename);  });};

用后盾的 filename 的话,能够从 header 的 content-disposition 中去获取。

4、如何实现批量下载,且打包文件

在第 3 点的根底上,如果要实现批量下载,那能做到的只是间断屡次调用 download 办法,这样无奈批量集中的下载文件。这个时候就须要可能对已获取到的文件流,进行一个打包的操作,而后一次下载结束。

这时,须要用到两个库 jszip 和 file-saver

残缺的思路:通过 ajax 获取文件,而后用 jszip 压缩文件, 再用 file-saver 生成文件

  • 获取文件 同上(第 3 点)
  • 打包文件
export const download = () => {  const urls = ["url", "url"]; //须要下载的门路  const zip = new JSZip();  const cache = {};  const promises = [];  urls.forEach((item) => {    const promise = getBlob(item).then((data) => {      // 下载文件, 并存成ArrayBuffer对象      zip.file("下载文件名", data, { binary: true }); // 一一增加文件      cache[item.fileName] = data;    });    promises.push(promise);  });  Promise.all(promises).then(() => {    zip.generateAsync({ type: "blob" }).then((content) => {      // 生成二进制流      FileSaver.saveAs(content, `打包下载.zip`); // 利用file-saver保留文件    });  });};

残缺代码

/** * 获取文件 * @param url * @returns {Promise<any>} */const getBlob = (url) => {  return new Promise((resolve) => {    const xhr = new XMLHttpRequest();    xhr.open("GET", url, true);    xhr.responseType = "blob";    xhr.onload = () => {      if (xhr.status === 200) {        resolve(xhr.response);      }    };    xhr.send();  });};/** * 批量打包zip包下载 * @param urlArr Array [{url: 下载文件的门路, fileName: 下载文件名称}] * @param filename zip文件名 */export const download = (urlArr, filename = "打包下载") => {  if (!urlArr.length > 0) return;  const zip = new JSZip();  const cache = {};  const promises = [];  urlArr.forEach((item) => {    const promise = getBlob(item.url).then((data) => {      // 下载文件, 并存成ArrayBuffer对象      zip.file(item.fileName, data, { binary: true }); // 一一增加文件      cache[item.fileName] = data;    });    promises.push(promise);  });  Promise.all(promises).then(() => {    zip.generateAsync({ type: "blob" }).then((content) => {      // 生成二进制流      FileSaver.saveAs(content, `${filename}.zip`); // 利用file-saver保留文件    });  });};

留神:因为通过浏览器进行打包压缩,如果文件过大,或者下载的内容过多,可能导致浏览器解体。

总结

综上所述,前端下载文件的计划多种多样,开发者应依据具体需要和场景抉择适合的计划。对于简略的动态文件下载,能够间接应用<a>标签;对于动静生成的文件或须要通过Ajax申请获取的文件,能够应用JavaScript或第三方库来实现下载性能。无论抉择哪种计划,都应留神文件的安全性、兼容性和性能问题,以确保良好的用户体验。

本文由mdnice多平台公布