前端主流的几种下载文件的方式

124 阅读5分钟

一、直接使用<a>标签下载

如果文件部署到了服务器上,可以通过接口拿到文件的url,直接使用<a>标签进行下载。

<a href="/user/test/xxxx.xls"` `download="文件名.xls">点击下载</a>

注意:

  • 这种方法的弊端就是需要网站与资源url同源,跨域的话会跳转至新页面并预览,例如下载的如果是视频会打开新窗口进行播放,音频也会打开新窗口进行播放,txt/doc/pdf/jpg同理,如果下载的是游览器不支持,才会下载。
  • 非同源的文件可以告知用户使用鼠标右键调用迅雷、IDM之类的第三方软件进行下载

二、如果文件是通过接口返回

1、Blob对象

Blob对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File接口基于Blob,继承了blob的功能并将其扩展使其支持用户系统上的文件。

(1)构造函数

var aBlob = new Blob( array, options );

array 是一个由ArrayBuffer(二进制数据缓冲区)、ArrayBufferView(二进制数据缓冲区的array-like视图)、Blob、DOMString等对象构成的Array,或者其他类似对象的混合体,它将会被放进Blob。DOMStrings会被编码为UTF-8。

options 是可选的,它可能会指定如下两个属性:

  • type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型,下载指定扩展名的文件只需要对照MIME 参考手册设置type即可。

  • endings,默认值为"transparent",用于指定包含行结束符n的字符串如何被写入(一般不使用)。 它是以下两个值中的一个: "native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持blob中保存的结束符不变。

(2)示例

var debug = { hello: "world" };
var blob = new Blob([JSON.stringify(debug)], { type : 'application/json' });

2、URL对象

(1)通过创建URL对象指定文件的下载链接。

objectURL = window.URL.createObjectURL(blob);

(2)window.URL.revokeObjectURL()

在每次调用createObjectURL()方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL()方法来释放。浏览器会在文档退出的时候自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。

window.URL.revokeObjectURL(objectURL);

3、利用<a>标签下载

*没必要将<a>标签添加到dom中(document.body.appendChild(link))

(1)生成一个<a>标签。

const link = document.createElement('a');

(2)href属性指定下载链接

link.href = window.URL.createObjectURL(blob);

(3)dowload属性指定文件名

download 属性规定被下载的超链接目标。在<a>标签中必须设置 href 属性。该属性也可以设置一个值来规定下载文件的名称。所允许的值没有限制,浏览器将自动检测正确的文件扩展名并添加到文件 (.img, .pdf, .txt, .html, 等等)。

link.download = fileName;

(4)click()事件触发下载

link.click();

4、格式转换

MIME类型使用text/plain,用.txt文件的格式编码去下载doc(docx)文件(doc(docx)文件每次打开需要选择合适的编码,暂未找到解决方案,欢迎补充)。

const foo = { hello: "world" };
const blob = new Blob([JSON.stringify(foo)], { type: "text/plain" });
const fileName = `${new Date().valueOf()}.doc`;
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
// 没必要将<a>标签添加到dom中:document.body.appendChild(link)
link.click();
window.URL.revokeObjectURL(link.href);

5、请求失败处理

axios请求下载导出一个文件,请求成功时返回的是一个流形式的文件,需要设置responseType: 'arraybuffer',但是请求失败的需要返回的是json数据,所以需要把arraybuffer转成Json对象。

例如:

请求设置了responseType: 'arraybuffer',

请求成功时,下载文件,

请求失败时,后端返回json对象,如:{"msg":"系统异常","code":1,"success":false},也被转成了arraybuffer,

解决方案是,失败时,将数据arraybuffer转成Json对象就好了。

实例一(使用接口反参来判断失败)

根据接口返回type来判断调用成功,如果是 'application/json' 返回json字符串则说明调用失败。

(要和后端达成一致)

// 点击后触发
onClickDownload = (id, fileName) => {
  const options = {
    url: 'xxxxxx',
    method: 'get',
    data:{ id },
    responseType: 'blob', // 也可以使用arraybuffer,跟后端达成一致
  };
  
  axios(options).then(res => {
    const blob = new Blob([res]);
    
    // 接口失败处理,使用哪个字段主要看接口定义
    if (res.type === 'application/json') {
      const reader = new FileReader(); // 使用FileReader对象来读取文件
      
      // 文件读取成功完成时触发
      reader.onload = () => {
      	const resData = JSON.parse(reader.result);
        
        if (resData.code === -999 || resData === -998) {
          // 如果是 -999/-998 说明登录失效跳转到登录页面(根据接口定义来)
        	window.location.href = 'https://www.xxx/login';
        } else {
          // 其他错误使用antd的message给用户提示
          message.error(resData.message);
        }
        
      };
      
      reader.readAsText(blob); // 将文件读取为文本
    } else {
    	const link = document.createElement('a');
      link.href = URL.createObjectURL(blob); // window进行了省略(window.URL)
      
      // 这里就不存在跨域问题,因为文件内容前端自已进行了缓存,href是自己生成的
      link.download = fileName; // 设置文件名
      linck.click(); // 触发下载
      URL.revokeObjectURL(link.href); // 释放URL对象
    }
  });
}

实例二(使用try-catch捕获错误)

处理这种特殊返回值时需要设置axios的responseType为blob,防止axios内部默认处理返回值

axios.get({
  url: 'xxxxxx',
  method: 'get',
  data:{},
  responseType: 'blob'
}).then(res => {
  let data = res.data;
  let fileReader = new FileReader();
  fileReader.onload = function() {
    
    try {
      let jsonData = JSON.parse(this.result);
      
      if (jsonData.code) {
        // 说明是普通对象数据,后台转换失败
        // to do something
        alert('not ok');
      }
    } catch (err) {
      // 解析成对象失败,说明是正常的文件流
      alert('ok');
    }
  };
  
  fileReader.readAsText(data);
});

实例三(URL 转成 Blob 进行下载)

  const dataURLToBlob = (dataURL, type) => {
    // 代码参考自:https://github.com/ebidel/filer.js
    const parts = dataURL.split(';base64,');
    const contentType = parts[0].split(':')[1]; // 原始类型
    const raw = window.atob(parts[1]);
    const rawLength = raw.length;
    const uInt8Array = new Uint8Array(rawLength);

    for (let i = 0; i < rawLength; i++) {
      uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], { type: type || contentType });
  };

  const download = (dataURL, filename) => {
    const blob = dataURLToBlob(dataURL);
    const url = window.URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.style = 'display: none';
    a.href = url;
    a.download = filename;

    document.body.appendChild(a);
    a.click();

    window.URL.revokeObjectURL(url);
  };

三、使用相关插件进行下载

downloadjs、github搜索download

参考资料

segmentfault.com/a/119000001…

www.cnblogs.com/zhusf/p/111…

www.cnblogs.com/pingan8787/…

blog.csdn.net/dongguan_12…