返回

前端文件下载:如何添加自定义请求头?

javascript

在网页开发中,我们经常需要处理文件下载。当下载目标是大型二进制文件,并且服务器要求特定的请求头(例如 Authorization 用于身份验证)时,事情就变得有点复杂了。浏览器默认的下载行为通常无法直接添加自定义请求头,这给开发者带来了不少困扰。

简单的下载方式,比如直接用 a 标签链接到文件地址,或者使用 JavaScript 的 window.location.href 跳转,都无法满足添加自定义请求头的需求。这是因为浏览器在处理这些下载请求时,不会像普通 AJAX 请求那样允许我们自由地设置请求头。

为了解决这个问题,我们需要一些特殊的技巧。下面介绍两种常用的解决方案,它们都能帮助我们在下载大型二进制文件时添加自定义请求头。

方法一:利用 Blob 和 URL.createObjectURL

这种方法的核心思想是,先通过 JavaScript 的 XMLHttpRequestFetch API 发起请求,获取文件内容,并将其转换为 Blob 对象。然后,我们使用 URL.createObjectURL 创建一个临时的 URL,指向这个 Blob 对象。最后,我们创建一个 a 标签,将其 href 属性设置为这个临时 URL,并模拟点击操作触发下载。

fetch('/your-file-url', {
  headers: {
    'Authorization': 'Bearer your-token' // 替换成你的 token
  }
})
.then(response => response.blob())
.then(blob => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'your-file-name.ext'; // 设置下载文件名
  document.body.appendChild(a);
  a.click();
  URL.revokeObjectURL(url); // 释放 URL 对象
  document.body.removeChild(a); // 移除 a 标签
})
.catch(error => {
  console.error('下载失败:', error);
});

这段代码首先使用 Fetch API 发起请求,并在请求头中添加了 Authorization。接着,它将服务器返回的数据转换为 Blob 对象,并使用 URL.createObjectURL 创建一个指向 Blob 的临时 URL。最后,它动态创建了一个 a 标签,设置其 href 属性为临时 URL,download 属性为文件名,并模拟点击行为触发下载。下载完成后,我们还释放了临时 URL,并移除了 a 标签。

这种方法的优点在于它不需要引入额外的库,并且兼容性良好。但它有一个缺点:需要将整个文件加载到内存中,如果文件非常大,可能会导致性能问题。

方法二:使用 Service Worker

Service Worker 是浏览器后台运行的脚本,可以拦截网络请求并进行修改。我们可以利用 Service Worker 在请求发出前添加自定义请求头。

首先,我们需要在网页中注册 Service Worker:

// 在你的网页 JavaScript 代码中
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(() => console.log('Service Worker 注册成功'))
    .catch(error => console.error('Service Worker 注册失败:', error));
}

然后,在 Service Worker 脚本 (service-worker.js) 中,我们监听 fetch 事件,并在事件处理函数中修改请求头:

// service-worker.js
self.addEventListener('fetch', event => {
  if (event.request.url.endsWith('/your-file-url')) { // 替换成你的文件 URL
    event.respondWith(
      fetch(event.request, {
        headers: {
          ...event.request.headers,
          'Authorization': 'Bearer your-token' // 添加 Authorization 头
        }
      })
    );
  }
});

这段代码会在 Service Worker 中监听 fetch 事件。当请求的 URL 匹配你的文件 URL 时,它会使用 fetch 重新发起请求,并在请求头中添加 Authorization

这种方法的优点是可以拦截所有匹配的请求,并且不需要修改页面代码。但它需要你了解 Service Worker 的相关知识,并且兼容性略逊于第一种方法。

选择哪种方法?

两种方法各有优缺点,选择哪种取决于你的具体需求和项目情况。

  • 如果文件大小适中,并且你希望代码简单易懂,可以选择第一种方法(Blob 和 URL.createObjectURL)。
  • 如果你需要拦截所有匹配的请求,并且对 Service Worker 有一定了解,可以选择第二种方法(Service Worker)。

常见问题解答

  1. 为什么不能直接在 a 标签上添加自定义请求头?

    浏览器在处理 a 标签下载时,不会像 AJAX 请求那样允许我们自由地设置请求头。它只会发送基本的 GET 请求,无法添加自定义头信息。

  2. Blob URL 的有效期是多久?

    Blob URL 的有效期是文档的生命周期。当文档关闭或刷新时,Blob URL 就失效了。为了避免内存泄漏,建议在使用完 Blob URL 后,使用 URL.revokeObjectURL 释放它。

  3. Service Worker 的兼容性如何?

    Service Worker 的兼容性已经相当不错,大多数主流浏览器都支持它。但一些旧版本的浏览器可能不支持。

  4. 如何调试 Service Worker?

    你可以打开浏览器的开发者工具,在 "Application" 选项卡中找到 "Service Workers" 面板,在这里可以查看 Service Worker 的状态、缓存内容等信息,也可以进行调试。

  5. 除了 Authorization,还可以添加其他自定义请求头吗?

    是的,你可以根据需要添加任何自定义请求头。例如,你可以添加 Range 头来实现断点续传。