前端文件下载全攻略:从单文件到批量下载,哪种方法最优?

0 阅读4分钟

小张是一名刚入职的前端开发工程师,某天,他的领导给他布置了一个看似简单的任务:

让用户能够通过文件链接下载多个文件

小张信心满满,觉得这不过是个小问题。然而,当他真正动手时,才发现这个需求并不简单。不同的下载方式各有优缺点,甚至有些方法会带来意想不到的问题,他决定一一尝试,探索最优解。

方案一:window.open——简单粗暴,但会打开新标签页

小张首先想到的是 window.open(url),它可以让浏览器直接打开下载链接。

window.open('https://example.com/file.pdf');

优点

  • 代码简单,直接调用即可。
  • 适用于单个文件的下载。

缺点

  • 每次下载都会打开一个新的浏览器标签页,影响用户体验。
  • 部分浏览器可能会拦截 window.open,导致下载失败。

方案二:window.location.href 简单有效,但不能同时下载多个文件

小张发现,window.location.href 也可以实现下载,且不会打开新标签页。

window.location.href = 'https://example.com/file.pdf';

优点

  • 适用于单文件下载。
  • 不会像 window.open 那样打开新页面。

缺点

  • 无法循环下载多个文件。如果连续多次赋值 window.location.href,后一个请求会覆盖前一个,导致只能下载最后一个文件。

方案三:iframe 支持多文件下载,但无法监听完成状态

为了让多个文件能够顺利下载,小张尝试用 iframe

function downloadFile(url) {
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  iframe.src = url;
  document.body.appendChild(iframe);
  setTimeout(() => {
    document.body.removeChild(iframe);
  }, 5000); // 延迟移除 iframe,防止影响下载
}

优点

  • 适用于多文件下载。

缺点

  • iframe 无法监听文件下载是否完成。
  • 需要在合适的时机移除 iframe,否则可能会影响页面性能。

方案四:fetch + blob——最优雅的下载方式

小张最终发现,fetch 可以获取文件数据,再通过 Blob 处理并使用 a 标签下载。

async function downloadFile(url, fileName) {
  const response = await fetch(url);
  if (!response.ok) throw new Error('Download failed');
  const blob = await response.blob();
  const blobUrl = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = blobUrl;
  a.download = fileName;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(blobUrl);
}

function download(fileList){
   for(const file of fileList) {
     await downloadFile(file.url,file.name)
   }
}

优点

  • 不会打开新标签页。
  • 可以同时下载多个文件。
  • 适用于现代浏览器,兼容性较好。

缺点

  • 需要处理异步 fetch 请求。
  • 服务器必须支持跨域资源共享(CORS),否则 fetch 请求会失败。
  • 多次文件下载会导致多个浏览器下载图标:每次调用 a.click() 时,浏览器都会显示一个下载图标,影响用户体验。

方案五:jsZip 打包多个文件为 ZIP 下载——避免多次下载图标

为了进一步优化方案四,避免浏览器每次下载时显示多个下载图标,小张决定使用 jsZip 插件将多个文件打包成一个 ZIP 文件下载。

import JSZip from 'jszip';

async function downloadFilesAsZip(files) {
  const zip = new JSZip();
  // 循环遍历多个文件,获取每个文件的数据
  for (const file of files) {
    const response = await fetch(file.url);
    if (!response.ok) throw new Error(`Failed to fetch ${file.name}`);
    const blob = await response.blob();
    zip.file(file.name, blob); // 将文件添加到 ZIP 包中
  }

  // 生成 ZIP 文件并触发下载
  zip.generateAsync({ type: "blob" })
    .then(function(content) {
      const a = document.createElement('a');
      const blobUrl = URL.createObjectURL(content);
      a.href = blobUrl;
      // 给压缩包设置下载文件名
      a.download = 'files.zip';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      // 释放 URL 对象
      URL.revokeObjectURL(blobUrl);
    });
}

优点

  • 提升用户体验:用户下载一个压缩包后,只需解压就可以获取所有文件,避免了多次点击和等待的麻烦。
  • 适用于多文件下载:非常适合需要批量下载的场景。

缺点

  • 浏览器对大文件的支持:如果要下载的文件非常大,或者文件总大小很大,可能会导致内存消耗过高,甚至在浏览器中崩溃。
  • 下载速度受限于压缩处理:打包文件为 ZIP 需要时间,尤其是文件较多时,会稍微影响压缩的速度,只适用于文件不是很大且数量不是很多的时候

结语:小张的最终选择

经过一番探索,小张最终选择了 jsZip 打包文件的方案,因为它不仅解决了多个文件下载时图标显示的问题,还提高了用户体验,让下载更加流畅,没有哪个方案比另外一个方案好,只有最适合的方案,根据实际的场景能满足需求最优解就是最好的。