• 前端文件下载方法总结


    前端文件下载方法总结

    1、open或location.href

    最简单最直接的方式,跟a标签访问下载链接一样
    前提:下载地址不需要鉴权 以及 不存在跨域问题

    window.location.href = url // url为下载地址
    window.open('downloadFile.zip');
    
    • 1
    • 2

    优点:简单方便

    缺点:

    • 直接访问可能会覆盖当前页面地址
    • 不能添加header,也就不能进行鉴权
    • 会出现URL长度限制问题
    • 需要注意url编码问题
    • 浏览器可直接浏览的文件类型是不提供下载的,如txt、png、jpg、gif、pdf等
    • 无法知道下载的进度

    2、用a标签下载

    a标签可以访问下载文件的地址。如果 是对于浏览器可以直接浏览的文件类型:jpg\png\gif\txt等,a标签不能直接下载。

    所以,在html5中,a标签提供了 download属性。

    简单用法:

    <a href="example.jpg" download>点击下载</a>
    
    //如果带属性值 指定下载的文件名,即重命名下载文件。不设置的话默认是文件原本名。
    <a href="example.jpg" download="test">点击下载</a>
    
    • 1
    • 2
    • 3
    • 4

    动态a标签:
    下面的url即文件或接口的地址
    如需要额外参数,通过url后问号拼接参数,后端get请求方式接收

    服务端需要配置url资源的响应头Content-disposition值为attachment,浏览器识别会调用下载弹窗,进一步配置filename值即下载的默认文件名。

    // 封装
    function downloadFile (url, name) {
      url = url || ''
      name = name || ''
      let ele = document.createElement('a')
      ele.target = '_blank'
      ele.href = url
      ele.download = name
      ele.click()
      ele = null
    }
    
    // 调用
    downloadFile(url)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    判断浏览器是否支持download属性:

    const isSupport = 'download' in document.createElement('a');
    
    • 1

    需要注意一些信息:

    • Edge 13在尝试下载data url链接时会崩溃。
    • Chrome 65及以上版本只支持同源下载链接。
    • Firefox只支持同源下载链接。

    基于上面描述,如果你尝试下载跨域链接,那么其实download的效果就会没了,跟不设置download表现一致。即浏览器能预览的还是会预览,而不是下载。

    对于在跨域下不能下载可浏览的文件,得跟后端协商,在后端层做多一层转发,最终返回给前端的文件链接跟下载页同域。

    优点:download属性能解决不能直接下载浏览器可浏览的文件

    缺点:

    • 不能下载跨域下的浏览器可浏览的文件
    • 不能进行鉴权
    • url资源或接口响应头配置了filename时,前端无法自定义下载文件名。
    • 有兼容性问题,特别是IE
    • 前端需要自定义展示下载进度条需求时无法支持。

    3、利用Blob下载,发送ajax请求api获取文件流进行下载

    这种方法除了能利用已知文件地址路径进行下载外,还适用于需要调取API获取文件流下载的形式。
    有些时候后端不会直接提供一个下载地址给你直接访问,而是要调api。

    方法:

    前端请求接口设置responseType: 'blob', 统一 转为Blob 数据,然后利用URL.createObjectUrl生成url地址,赋值在a标签的href属性上,结合download进行下载.

    blob 是 js 里表示二进制文件的对象。

    target.response就是一个Blob对象,打印出来会看到两个属性size和type。

    2种方法代码参考

    文件下载方法参考,第二种可设置下载进度。
    参考:https://blog.csdn.net/u010059669/article/details/122623034

    /**
     * 第一种方法:
     * @param {String} path - 下载地址/下载请求地址。
     * @param {String} name - 下载文件的名字/重命名(考虑到兼容性问题,最好加上后缀名)
     */
    downloadFile (path, name) {
        const xhr = new XMLHttpRequest();
        xhr.open('get', path);
        xhr.responseType = 'blob';
        xhr.send();
        xhr.onload = function () {
            if (this.status === 200 || this.status === 304) {
                // 如果是IE10及以上,不支持download属性,采用msSaveOrOpenBlob方法,但是IE10以下也不支持msSaveOrOpenBlob
                if ('msSaveOrOpenBlob' in navigator) {
                    navigator.msSaveOrOpenBlob(this.response, name);
                    return;
                }
                // const blob = new Blob([this.response], { type: xhr.getResponseHeader('Content-Type') });
                // const url = URL.createObjectURL(blob);
                const url = URL.createObjectURL(this.response);
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                a.download = name;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }
        };
    }
    
    /**
     * 第二种方法:
     * @description: ajax下载/导出文件
     * @param {string} url 资源或接口地址,可携带参数
     * @param {function} progressCallback 选填,前端自定义的下载进度回调
     */
    function downloadFromApi (url, progressCallback) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open('get', url)
        xhr.responseType = 'blob'
        xhr.onload = e => {
          if (xhr.status === 200) {
            const response = xhr.response
            resolve(response)
    
            const dispoition = xhr.getResponseHeader('Content-Disposition') || ''
            const nameStr = dispoition.split(';')[1] || ''
            const fileName = decodeURIComponent(nameStr.split('=')[1] || '')
    
            if (window.navigator.msSaveOrOpenBlob) {
              // 兼容处理,ie下的blob下载
              window.navigator.msSaveOrOpenBlob(response, fileName)
            } else {
              const href = URL.createObjectURL(response)
    		  let ele = document.createElement('a')
    		  ele.target = '_blank'
    		  ele.href = href
    		  ele.download = fileName
    		  ele.click()
    		  ele = null
    		  URL.revokeObjectURL(href)
            }
          } else {
            reject(new Error(`${xhr.status}:请求失败`))
          }
        }
        xhr.error = err => {
          reject(err)
        }
        xhr.onprogress = e => {
          if (e.lengthComputable) {
            const percentComplete = e.loaded / e.total
            progressCallback && progressCallback(percentComplete)
          }
        }
        xhr.send(null)
      })
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    下载进度设置的原理

    服务端需要配置响应头的Content-Length,值为文件大小(未配置的话会在文件传输完成后才弹窗提示下载,响应感知太慢)。

    前端监听xhr对象的progress事件:

    xhr.onprogress = e => {
      if (e.lengthComputable) {
        const percentComplete = e.loaded / e.total
        // 打印当前已完成的进度比例
        console.log(percentComplete)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    另一种封装方式(axios 请求)

    如果是封装了axios请求,可以在调请求后,使用下面的封装的downLoadFile 方法,使用示例:

    /*
    httpRequest: 封装的下载API请求,记得设置responseType = 'blob'
    url: 接口
    */
     httpRequest(url).then(async (res) => {
       console.log('下载请求返回:', res);
       const { data, response } = res;
       if (data && response.status === 200) {
       	// 调用封装好的下载文件的方法
         downLoadFile(data, response);
       } 
     }).catch(async (err) => {
       console.log(err);
     });
     
    /**
     * 下载文件
     * @param {any} data - 下载请求返回blob对象的data, 打印出来会有一个size和type 属性。
     * @param {Response} response- 请求的response
     */
    const downLoadFile = (data: any, response: Response) => {
      let contentType = response.headers.get('content-type') || undefined;
      let contentDisposition = response.headers.get('content-disposition') || undefined;
    
      contentType = isExistValue(contentType) ? contentType : undefined;
      contentDisposition = isExistValue(contentDisposition) ? contentDisposition : undefined;
    
    	// 文件名提取
      let filename = 'xxx';
      if (typeof contentDisposition === 'string') {
        contentDisposition = valueToLowerCase(contentDisposition);
        try {
          // 文件名提取
          filename = contentDisposition.match(/filename=(.*)/)[1];
        } catch (err) {
          console.log(err);
          // @ts-ignore
          filename = contentDisposition;
        }
      }
      // 创建一个 Blob 对象
      const blob = new Blob([data], { type: contentType });
      // @ts-ignore
      if (typeof window.navigator.msSaveBlob !== 'undefined') {
        // 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
        window.navigator.msSaveBlob(blob, decodeURI(filename));
      } else {
        const href = window.URL.createObjectURL(blob);
        const eLink = document.createElement('a'); // 创建一个标签
        eLink.style.display = 'none'; // 隐藏标签
        eLink.href = href; // 配置href,指向本地文件的内存地址
        eLink.download = decodeURI(filename);
        document.body.appendChild(eLink);
        eLink.click();
        // 释放URL 对象
        document.body.removeChild(eLink);
        // 释放掉blob对象
        URL.revokeObjectURL(href);
      }
    };
    

    图片预览

    如果是图片预览,也是后端返回的地址,需要带上token去请求。需要用到base64。伪代码参考思路:

    const [imgSrc, setImgSrc] = useState('');// 图片预览的src
    
    useEffect(() => {
     const url = 'xxx.xx.xx/img/wx.png';
      // httpRequest自己封装的axios请求 , responseType 为 'blob'
      httpRequest(url).then(async (res) => {
        console.log('下载返回:', res);
        const { data, response } = res;
        if (data && response.status === 200) {
          // 生成图片预览的src
          const src = await blobToBase64(data);
          setImgSrc(src);
        }
      });
    }, []);
    
    
    /**
     * Blob 图像对象预览
     * */
    const blobToBase64 = (file: Blob) => {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = () => resolve(reader.result);
        reader.onerror = (error) => reject(error);
      });
    };
    
    // 在Image标签中使用:
    <Image width="80%" alt={name} src={imgSrc} />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    优点:

    • 能解决不能直接下载浏览器可浏览的文件
    • 可添加鉴权信息

    缺点: 兼容性问题,IE10以下不可用

  • 相关阅读:
    单元测试的心法分享
    卵清蛋白壳聚糖纳米粒的制备,OVA-CS纳米粒
    版本号正则校验及大小比较
    webpack5基础和配置
    1.4_16 Axure RP 9 for mac 高保真原型图 - 案例15 【动态面板-滚动条5】深色模式 - 按钮效果升级
    【python学习】基础篇-常用第三方库-chardet:检测文本文件的编码格式
    运营网课之活动运营
    【Java 面试题】经典 Java 面试题 200 问(上)
    MYSQL——二、理论基础
    Mini小主机All-in-one搭建教程1-安装Esxi7.0虚拟机系统
  • 原文地址:https://blog.csdn.net/yqdid/article/details/134293103