前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端下载文件的5种方法的对比

前端下载文件的5种方法的对比

作者头像
歪马
发布2020-04-07 11:55:08
6.6K0
发布2020-04-07 11:55:08
举报
文章被收录于专栏:歪码行空歪码行空歪码行空

前言

在前端站点上下载文件,这是一个极其普遍的需求,很早前就已经有各种解决方法了,为什么还写这么老的文章,只是最近在带一个新人,他似乎很多都一知半解,也遇到了我们必经问题之“不能下载txt、png等文件”的典型问题,我就给他总结下下载的几个方式。顺便分享出来,也许,真有人需要。

form表单提交

这是以前常使用的传统方式,毕竟那个年代,没那么多好用的新特性呀。

道理也很简单,为一个下载按钮添加 click事件,点击时动态生成一个表单,利用表单提交的功能来实现文件的下载(实际上表单的提交就是发送一个请求)

来看下如何生成一个表单,生成怎么样的一个表单:

/**
 
 * 下载文件
 
 * @param {String} path - 请求的地址
 
 * @param {String} fileName - 文件名
 
 */
 
function downloadFile (downloadUrl, fileName) {
 
 // 创建表单
 
 const formObj = document.createElement('form');
 
    formObj.action = downloadUrl;
 
    formObj.method = 'get';
 
    formObj.style.display = 'none';
 
 // 创建input,主要是起传参作用
 
 const formItem = document.createElement('input');
 
    formItem.value = fileName; // 传参的值
 
    formItem.name = 'fileName'; // 传参的字段名
 
 // 插入到网页中
 
    formObj.appendChild(formItem);
 
    document.body.appendChild(formObj);
 
    formObj.submit(); // 发送请求
 
    document.body.removeChild(formObj); // 发送完清除掉
 
}
 
优点
  • 传统方式,兼容性好,不会出现URL长度限制问题
缺点
  • 无法知道下载的进度
  • 无法直接下载浏览器可直接预览的文件类型(如txt/png等)

open或location.href

最简单最直接的方式,实际上跟 a标签访问下载链接一样

window.open('downloadFile.zip');
 
location.href = 'downloadFile.zip';

当然地址也可以是接口api的地址,而不单纯是个链接地址。

优点
  • 简单方便直接
缺点
  • 会出现URL长度限制问题
  • 需要注意url编码问题
  • 浏览器可直接浏览的文件类型是不提供下载的,如txt、png、jpg、gif等
  • 不能添加header,也就不能进行鉴权
  • 无法知道下载的进度

a标签的download

我们知道, a标签可以访问下载文件的地址,浏览器帮助进行下载。但是对于浏览器支持直接浏览的txt、png、jpg、gif等文件,是不提供直接下载(可右击从菜单里另存为)的。

为了解决这个直接浏览不下载的问题,可以利用 download属性。

download属性是HTML5新增的属性,兼容性可以了解下 can i use download

总体兼容性算是很好了,基本可以区分为IE和其他浏览。但是需要注意一些信息:

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

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

简单用法:

<a href="example.jpg" download>点击下载</a>

可以带上属性值,指定下载的文件名,即重命名下载文件。不设置的话默认是文件原本名。

<a href="example.jpg" download="test">点击下载</a>

如上,会下载了一个名叫 test的图片

监测是否支持download

要知道浏览器是否支持 download属性,简单的一句代码即可区分

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

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

优点
  • 能解决不能直接下载浏览器可浏览的文件
缺点
  • 得已知下载文件地址
  • 不能下载跨域下的浏览器可浏览的文件
  • 有兼容性问题,特别是IE
  • 不能进行鉴权

利用Blob对象

该方法较上面的直接使用 a标签 download这种方法的优势在于,它除了能利用已知文件地址路径进行下载外,还能通过发送ajax请求api获取文件流进行下载。毕竟有些时候,后端不会直接提供一个下载地址给你直接访问,而是要调取api。

利用 Blob对象可以将文件流转化成 Blob二进制对象。该对象兼容性良好,需要注意的是

  • IE10以下不支持。
  • 在Safari浏览器上访问 BlobUrlObjectURL是有缺陷的,如下文中通过 URL.createObjectURL生成的链接

第二点,这就导致这里以下的方案需留意适用于Safari的使用。希望日后Safari能修复该问题(当前我所测试版本为13.0.4 )。

进行下载的思路很简单:发请求获取二进制数据,转化为 Blob对象,利用 URL.createObjectUrl生成url地址,赋值在 a标签的 href属性上,结合 download进行下载。

/**
 
 * 下载文件
 
 * @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) {
 
 // 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);
 
 }
 
 };
 
}

该方法不能缺少 a标签的 download属性的设置。因为发请求时已设置返回数据类型为 Blob类型( xhr.responseType='blob'),所以 target.response就是一个 Blob对象,打印出来会看到两个属性 sizetype。虽然 type属性已指定了文件的类型,但是为了稳妥起见,还是在 download属性值里指定后缀名,如Firefox不指定下载下来的文件就会不识别类型。

大家可能会注意到,上述代码有两处注释,其实除了上述的写法外,还有另一个写法,改动一丢丢。如果发送请求时不设置 xhr.responseType='blob',默认ajax请求会返回 DOMString类型的数据,即字符串。这时就需要两处注释的代码了,对返回的文本转化为 Blob对象,然后创建blob url,此时需要注释掉原本的 consturl=URL.createObjectURL(target.response)

优点
  • 能解决不能直接下载浏览器可浏览的文件
  • 可设置header,也就可添加鉴权信息
缺点
  • 兼容性问题,IE10以下不可用;Safari浏览器注意使用

利用base64

这里的用法跟上面用 Blob大同小异,基本上思路是一样的,唯一不同的是,上面是利用 Blob对象生成 BlobURL,而这里则是生成 DataURL,所谓 DataURL,就是 base64编码后的url形式。

/**
 
 * 下载文件
 
 * @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) {
 
 const fileReader = new FileReader();
 
            fileReader.readAsDataURL(this.response);
 
            fileReader.onload = function () {
 
 const a = document.createElement('a');
 
                a.style.display = 'none';
 
                a.href = this.result;
 
                a.download = name;
 
                document.body.appendChild(a);
 
                a.click();
 
                document.body.removeChild(a);
 
 };
 
 }
 
 };
 
}
 

这里额外提供个方法,该方法作用是,当你知道文件的全名(含后缀名),想要重命名,但是得后缀名一样,来获取后缀名。

function findType (name) {
 
 const index = name.lastIndexOf('.');
 
 return name.substring(index + 1);
 
}
优点
  • 较用 BlobURL,这个可在Safari上使用
  • 能解决不能直接下载浏览器可浏览的文件
  • 可设置header,也就可添加鉴权信息
缺点
  • 兼容性问题,IE10以下不可用
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 歪码行空 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • form表单提交
  • open或location.href
  • a标签的download
  • 利用Blob对象
  • 利用base64
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档