关于前端:关于前端实现文件下载功能

4次阅读

共计 4826 个字符,预计需要花费 13 分钟才能阅读完成。

在 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 多平台公布

正文完
 0