前端开发中,实现文件下载的几种方法总结

337 阅读6分钟

前端开发中,实现文件下载的几种方法总结

本文章主要用于个人开发经验的积累记录与提供一些技术参考。同时欢迎大家学习参考与友谊指正,共同进步。

一、a标签实现文件下载

<a>标签的download属性是HTML5标准新增的属性,download属性的值可以指定下载文件的名称。

    <!-- 其中url为文件下载地址,fileName为下载文件名 -->
    <a href="url" download="fileName"></a>   

动态创建<a>标签,下载文件的写法:

function downloadFile(url,fileNmae){
    //创建a标签
    let a = document.createElement('a');
    //设置隐藏的a标签
    a.style = 'display: none'; 
    //设置下载文件的文件名
    a.download = filename;
    //设置文件下载地址
    a.href = url; 
    document.body.appendChild(a);
    // 触发a标签的click事件
    a.click();  
    document.body.removeChild(a); 
}

二、location.href实现文件下载

使用浏览器的BOM对象location对象进行文件下载。

    //其中url为文件下载地址
    location.href="url";

三、window.open实现文件下载

使用浏览器window对象open属性进行文件下载。

    //其中url为文件下载地址
    window.open(url);

四、文件流实现下载文件

下载的文件内容只需要后端返回的现有的数据,这时候就可以是使用纯前端实现下载文件的功能来减小服务器资源和带宽的浪费。

主要思路:主要是将数据转换为Blob:URLsdata:URLs。通常需要前后端结合,后端返回文件流,前端发起请求将其处理成url链接的形式,然后创建a标签实现文件下载。支持多文件下载。

数据转换为Blob:URLsData:URLs的具体方法

    //数据转换为Blob:URLs或Data:URLs的方式
    //一、数据转换为Data:URLs
    //Data:URLs是前缀`data:的URL字符串:格式如下
    //data:<mediatype>;base64,<data>
    //1、对于文本类型,可以直接将数据拼接
    const dataURL = `data:text/plain;base64,` + textData;
    //2、通过window.btoa()方法
    //btoa()函数将二进制数据的字符串创建为base-64编码的ASCII字符串
    let str = new Blob([string]);  //string为字符串
    let dataURL = 'data:text/plain;base64,'+ btoa(str);
    //3、通过FileReader.readAsDataURL(blob)方法
    //对于File或Blob对象,可以使用FileReader.readAsDataURL()的方法转换为data:URLs
    let blob = new Blob([string]);
    let reader = new FileReader();
    //兼容写法
    reader.onload = function() {
      const dataUrl = is_chrome_ios 
                                      ? reader.result
                        : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;')
    }
    //传入被读取的blob对象
    reader.readAsDataURL(blob);
    
    //二、数据转换为blob:URLs
    //blob:URLs是URL.createObjectURL() 静态方法创建的一个 DOMString,其中包含一个表示参数中给出的对象的URL。
    //URL.createObjectURL()方法只能处理File或Blob对象,所以如果要生成blobURLs则必须将数据转换为blob对象或file对象。
    //1、如果数据不是File或Blob对象
    //使用以下方法要转为Blob对象,data是字符流数据
    let blob = new Blob([data]);
    //生成BlobURLs,即生成新的url对象,这个新的URL对象即为文件下载地址
    let BlobURL = URL.createObjectURL(blob);
    //2、如果数据时文件流或Blob对象,可以直接生成BlobURLs
    let BlobURL = URL.createObjectURL(blob);
    

Blob:URLsdata:URLs方法概念简介

URL.createObjectURL()静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL对象表示指定的 File 对象或 Blob 对象。

Blob对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。

注意:动态创建出来的BlobURLs需要手动调用URL.revokeObjectURL()销毁,否则会一直保留到页面关闭,为了获得最佳性能和内存使用状况,文件下载完毕时需要主动销毁释放。

readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend (en-US) 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。

代码实现

    // 文件下载
    downloadFile() {
      this.getBlob(url).then((res) => {
        this.saveAs(res.data, fileName);
      });
    },
    //通过文件下载的url获取对应的blob对象
    getBlob(url) {
      return new Promise((resolve) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        //设置请求头
        xhr.setRequestHeader("Content-type", "application/json");
        // 设置token
        xhr.setRequestHeader("Authorization", this.$store.getters.getToken());
        // 设置响应类型  若不指定,下载的文件打不开
        xhr.responseType = "blob";
        //设置动态加载状态
        this.loadingStatus = true;
        xhr.onload = () => {
          if (xhr.status === 200) {
            // 数据请求成功后,动态加载状态消失
            this.loadingStatus = false;
            resolve(xhr.response);
          }
        };
        xhr.send();
      });
    },
    //方式一 转为Blob:Urls的方式
    //模拟点击a标签进行下载
    saveAs(data, filename) {
      // 将数据转为blob对象
      let blob = new Blob([data]);
      let link_url = window.URL.createObjectURL(blob);
      //创建动态a标签,并设置相应属性
      let link = document.createElement("a");
      link.href = link_url;
      link.download = filename;
      document.body.appendChild(link);//添加a标签(可有可无,看需求)
      //模拟点击a标签
      link.click();
      document.body.removeChild(link);//移除a标签(可有可无,看需求。若移除a标签,则无需主动销毁Blob对象生成的url对象)
      //使用完毕,主动销毁释放blob对象生成的url对象
      window.URL.revokeObjectURL(link_url);
    }
    
    //方式二 转为Data:Urls的方式
    //模拟点击a标签进行下载
    saveAs(data, filename) {
      //创建FileReader文件读取对象
      const reader = new FileReader();
      //传入被读取的blob对象 (参数为即将被读取的Blob或File 对象)
      reader.readAsDataURL(data);
      // 读取完成后的回调事件
      reader.onload =function(e){
          // 生成的base64编码 
          let link_url = reader.result; //或者let link_url = e.target.result;
          //创建动态a标签,并设置相应属性
          let link = document.createElement("a");
          link.href = link_url;
          link.download = filename;
          document.body.appendChild(link);//添加a标签(可有可无,看需求)
          //模拟点击a标签
          link.click();
          document.body.removeChild(link);移除a标签(可有可无,看需求)
      }
    }

一些差异对比:Data:UrlsBlob:Urls

  • 主要区别

通过Blob:Urls的URL.createObjectURL()方法,可以获取当前文件的一个内存URL,性能相对更优。

通过Data:Urls的 FileReader.readAsDataURL()方法,可以获取一段data:base64的字符串。

  • 执行时机 Blob:Urls的URL.createObjectURL()方法,直接返回,同步执行。

Data:Urls的 FileReader.readAsDataURL()方法,需要回调函数,异步执行。

  • 适用范围 Blob:Urls的URL.createObjectURL()方法,因其特性原因,其此特性在 Web Worker 中可用;在 Service Worker 中不可用,因为它有可能导致内存泄漏。

Data:Urls的 FileReader.readAsDataURL()方法, 如果不太在意设备性能问题,并想获取图片的base64,则推荐使用FileReader.readAsDataURL

  • 回收机制 Blob:Urls的URL.createObjectURL()方法,返回一段带hash的url,并且一直存储在内存中,与documen绑定。直到document卸载或者主动执行revokeObjectURL()来释放。

Data:Urls的 FileReader.readAsDataURL()方法,则返回包含很多字符的base64,并会比Blob:Urls消耗更多内存,但是在不用的时候会自动从内存中清除(通过垃圾回收机制)。

  • 处理多文件

Blob:Urls的URL.createObjectURL()方法,处理多文件时依次返回,没有影响。

Data:Urls的 FileReader.readAsDataURL()方法,同时处理多个文件时,需要一个文件对应一个FileReader对象。

  • 兼容性

兼容性方面,主要考虑IE浏览器方面,两个属性方法都兼容IE10以上的浏览器。