完美的兼容性好的前端文件下载方式探讨.md 4.44 KB
Newer Older
liyan's avatar
liyan committed
1

liyan's avatar
liyan committed
2 3
---
title: 完美的兼容性好的前端文件下载方式探讨           
liyan's avatar
liyan committed
4
date: 2022/3/29 10:42:20
liyan's avatar
liyan committed
5 6 7 8 9 10
tags:                    
- 前端
- 文件下载
- JavaScript             
author: 李燕         
---
liyan's avatar
liyan committed
11 12 13 14 15

在项目中,经常会遇到文件下载的需求。一开始,都是copy既有项目的下载方法,用于自己的项目。但是,渐渐发现,copy过来的方法,有时会出现这样那样的问题,而且不同的项目,用的方法又是不一样的,没有一个通用的方法。

故感觉自己在文件下载这块,思绪还是乱的,没有一个清晰且系统概念。所以想通过本篇文章,好好地整理一下。

liyan's avatar
liyan committed
16
<!-- more -->
liyan's avatar
liyan committed
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

我们下载的文件,通常有三种形式:

- 有明确地址路径的文件
- 二进制数据文件
- base64文件



而根据浏览器的特性,又可以分为:

- 浏览器可直接浏览的文件,如txt、png、jpg、gif等格式的文件
- 浏览器不能直接浏览的文件,如doc、excel、zip等格式的文件



在下载方式这块,针对不同文件类型,常用的方式也不同。


**一、对于有明确地址路径的文件进行下载**



1. window API 实现下载

```js
window.open(fileUrl)

window.location.href = fileUrl
```

这两种方式虽然简单方便直接,但是浏览器能直接打开的文件只能预览,不能下载。



2. form表单实现下载

```js
const fromObj = document.createElement('form')
fromObj.action = fileUrl
formObj.method = 'get'
formObj.style.display = 'none'
const formItem = document.createElement('input')
formItem.value = fileName
formItem.name = 'fileName'
formObj.appendChild(formItem)
document.body.appendChild(formObj)
formObj.submit()
document.body.removeChild(formObj)
```

这是以前常用的传统方式,利用表单提交的功能来实现文件的下载。兼容性好,但是也无法下载浏览器能直接预览的文件。



3. a标签实现下载

```js
<a href="fileUrl"></a>
```

如果仅仅这样写,对于浏览器能直接打开的文件也是只能预览,不能下载。

要想能够直接下载浏览器能直接打开的文件,可以利用download属性。

```js
<a href="fileUrl" download="fileName"></a>
```

但download属性实现浏览器可预览文件下载也有限制:

- 同源,即所要下载的文件与当前页面同源。
- 非IE浏览器。

也就是说,如果不同源或是IE浏览器,即使加了download属性,也不能实现浏览器可预览文件的下载。



**二、对于二进制数据文件进行下载**

```
downFile(params).then(res=> {
 
      const fileName = res.headers['content-disposition'].split('=')[1];
 
      const data = res.data;
 
      const blob = new Blob([data]);
      
      //如果浏览器不支持download属性(也就是使用IE10及以上的时候,使用msSaveOrOpenBlob方法,但IE10以下也不支持msSaveOrOpenBlob
      
      if(window.navigator.msSaveOrOpenBlob) {
      
        window.navigator.msSaveOrOpenBlob(blob, fileName )
        return
      }
      //
      const a = document.createElement('a');
 
      const href = window.URL.createObjectURL(blob); // 创建下载的链接
 
      a.href = href;
 
      a.download = decodeURI(fileName); // 下载后文件名
 
      document.body.appendChild(a);
 
      a.click(); // 点击下载
 
      document.body.removeChild(a); // 下载完成移除元素
 
      window.URL.revokeObjectURL(href); // 释放掉blob对象
```

这种方式主要将文件流转换成Blob对象,并利用URL.createObjectURL生成url地址,然后再利用a标签下载。

但这种同样存在兼容性问题,IE10以下不可用。



**三、对于base64文件进行下载**

```js
downFile(fileBase64) {
      // fileBase64是获取到的图片base64编码
      const imgUrl = `data:image/png;base64,${fileBase64}`
      if (window.navigator.msSaveOrOpenBlob) {
        const bstr = atob(imgUrl.split(',')[1])
        let n = bstr.length
        const u8arr = new Uint8Array(n)
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n)
        }
        const blob = new Blob([u8arr])
        window.navigator.msSaveOrOpenBlob(blob, fileName + '.' + 'png')
      } else {
        const a = document.createElement('a')
        a.href = imgUrl
        a.setAttribute('download', fileName)
        a.click()
      }
}
```

IE10以下的兼容性问题依然存在。



以上文件下载方式,或多或少都存在一些限制或兼容性问题,有没有完美的解决方案,欢迎大佬们指教!