前端文件下载方式

423 阅读4分钟

前端文件有多种下载方式,首先,明确一个基本的概念。出于安全方面的考虑,浏览器中运行的Js代码是不可以“直接”对本地磁盘进行读写的,这个是浏览器的js引擎所限制的。但是由于浏览器本身作为一个应用程序,它是可以操作文件系统的。所以前端实现下载功能一定是依赖于浏览器的特性的,而不是js的特性。参考链接

a标签下载

<a download="文件名" href="文件下载接口地址">下载</a>

download的属性是HTML5新增的属性

href属性的地址必须是非跨域的地址,如果引用的是第三方的网站或者说是前后端分离的项目(调用后台的接口),这时download就会不起作用。

此时,如果是下载浏览器无法解析的文件,例如.exe,.xlsx..那么浏览器会自动下载,但是如果使用浏览器可以解析的文件,比如.txt,.png,.pdf....浏览器就会采取预览模式

所以,对于.txt,.png,.pdf等的预览功能我们就可以直接不设置download属性(前提是后端响应头的Content-Type: application/octet-stream,如果为application/pdf浏览器则会判断文件为 pdf ,自动执行预览的策略)

我们也可以通过click事件触发function动态的创建a标签来下载文件

export const downloadFile = (url, fileName?) => {
   const dwldLink = document.createElement('a');
   dwldLink.setAttribute('href', url);
   dwldLink.style.visibility = 'hidden';
   if(fileName) dwldLink.download = fileName;
   document.body.appendChild(dwldLink);
   dwldLink.click();
   // remove it after click
   document.body.removeChild(dwldLink);
}

url上设置response-content-disposition属性

在下载url后添加?response-content-disposition=attachment;filename=文件名,通过添加这个参数,服务器将识别该请求头,将响应内容作为文件进行下载,并使用指定的文件名命名下载的文件。

  • response-content-disposition=attachment:表示将服务器的响应内容以附件形式下载,而不是直接在浏览器中打开。
  • filename=文件名:指定下载的文件名,可以自定义文件名,确保用户下载时看到的文件名符合预期。

优点

  • 可以下载txt、png、pdf等类型文件
  • 下载文件将由浏览器管理,无需做额外处理

缺点

  • a标签只能做get请求,所有url有长度限制,并且无法携带body
  • 无法获取下载进度
  • 无法在header中携带token做鉴权操作
  • 无法判断接口是否返回成功

form标签下载

function inputDownloadEvt(method, url, paramsKey, paramsValue) {
      const form = document.createElement('form');
      form.style.display = 'none';
      form.setAttribute('target', '_blank');
      form.setAttribute('method', method);
      form.setAttribute('action', url);
      const input = document.createElement('input');
      input.setAttribute('type','hidden');
      // 对于get请求 最终会拼成http://192.168.66.183:13666/download?name=HAP.pdf
      input.setAttribute('name', paramsKey);
      input.setAttribute('value', paramsValue);
      form.appendChild(input);
      document.body.appendChild(form);
      form.submit();
      document.body.removeChild(form);
    }

优点

  • 下载文件将由浏览器管理,无需做额外处理
  • 兼容性好,不会出现URL长度限制问题
  • form标签get和post都可以,可以携带body

缺点

  • 无法获取下载进度
  • 无法在header中携带token做鉴权操作
  • 无法直接下载浏览器可直接预览的文件类型(txt、png、pdf会直接预览)
  • 无法判断接口是否返回成功

window.open 与 location.href 下载

window.open(url, '_self');
window.location.href = url;

优点

  • 简单方便直接
  • 下载文件将由浏览器管理,无需做额外处理

缺点

  • 会出现URL长度限制问题
  • 需要注意url编码问题
  • 无法获取下载进度
  • 无法在header中携带token做鉴权操作
  • 无法直接下载浏览器可直接预览的文件类型(txt、png、pdf会直接预览)
  • 无法判断接口是否返回成功

http下载

const downloadTask = (url, fileName, token) => {
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, true);
   xhr.responseType = "blob"; // 声明返回类型为二进制数据
   xhr.setRequestHeader("Authorization", token); // 设置自定义请求头
   xhr.addEventListener("progress", function(event) {
      if (event.lengthComputable) {
         const p = (event.loaded / event.total) * 100;
         // 设置下载进度
         setDownloadPercent(p.toFixed())
      }
   });
   xhr.onerror = onError;
   xhr.onload = function() {
      if (xhr.status === 200) {
         setDownloadPercent(100)
         const blob = new Blob([xhr.response], { type: "application/octet-stream" });
         const downloadUrl = URL.createObjectURL(blob);

         // 创建下载链接并点击触发下载
         const link = document.createElement("a");
         link.href = downloadUrl;
         link.download = fileName;
         link.click();

         // 清理对象 URL
         URL.revokeObjectURL(downloadUrl);

         setTimeout(close, 500);
      }
   };

   xhr.send();
}

前端获取到服务器端生成的字节流,此时数据是存在于js的内存中的,是不可以直接保存在本地的,利用Blob对象和window.URL.createObjectURL对象生成一个虚拟的URL地址,然后在利用浏览器的特性进行下载。 因此对于ajax下载大文件时,会出现浏览器崩溃情况,此时可以考虑使用链接直接下载或使用分片下载

优点

  • 可以下载txt、png、pdf等类型文件
  • 可以在header中携带token做鉴权操作
  • 可以获取文件下载进度
  • 可以判断接口是否返回成功

缺点

  • 将后端返回的文件流全部获取后才会下载
  • 下载文件过大,将会失败