浅析HTML5中的download属性使用


Posted in HTML / CSS onMarch 13, 2019

随着前端技术的发展,越来越多的业务场景中需要前端来处理文件下载。在众多的方法中,通过 <a> 标签的 download 属性实现下载是其中常见也是比较简单的一种方法。

download 属性介绍

常规的 <a> 标签通过 href 实现链接跳转,如果只想下载文件而不是跳转预览,最好的方式是在 <a> 标签中添加 download 属性,就能很简单地实现下载操作。

download 是 HTML5 中 <a> 标签新增的一个属性,此属性会强制触发下载操作,指示浏览器下载 URL 而不是导航到它,并提示用户将其保存为本地文件,例如:

<a href="result.png" download>download</a>

如果缺少 download 属性,点击 "download" 会直接变成预览图片,当添加 download 属性后则会触发图片的下载。

目前 download 属性的兼容性如caniuse 中所展示的:

浅析HTML5中的download属性使用

可以以看到,大部分主流的浏览器基本都已经支持 download 属性,而 IE 的表现一如既往的感人,目前许多 Window 系统仍然在使用 IE ,这也是许多业务需求需要考虑的。这种兼容性问题限制了 download 的更广泛应用。

动态资源下载

面对一些动态内容下载的业务场景,即图片等资源的地址并不是固定的(例如一些在线绘图工具所生成的图片),只使用 HTML 无法满足需求。为了能够满足不同的 URL 下载,可以通过JS 实现一个动态触发 URL 下载的方法。

function download(href, filename='')  {
  const a = document.createElement('a')
  a.download = filename
  a.href = href
  document.body.appendChild(a)  
  a.click()
  a.remove()
}

需要注意的是,代码中对创建的 <a> 进行的 appendChildremove 操作主要是为了兼容 FireFox 浏览器,在 FireFox 浏览器下调用该方法如果不将创建的 <a> 标签添加到 body 里,点击链接不会有任何反应,无法触发下载,而在 Chrome 浏览器中则不受此影响。

上述的方法可以实现同源资源的下载。但在很多场景中,还需要处理跨域资源。遗憾的是, download 属性目前仅适用于 同源 URL ,即如果需要下载的资源地址是跨域的, download 属性就会失效,点击链接会变成导航预览。

测试:点击下载,结果只是预览而无法下载图片。

注: Chrome65 之前是支持 download 属性触发文件跨域下载的,之后则严格遵循同源策略,无法再通过 download 属性触发跨域资源的下载。而 FireFox 一直不支持跨域资源的 download 属性下载。

文件命名问题

download 属性不仅可以触发下载,也能指定下载文件名:

<a href="test.png" download="joker.png">下载</a>

如果下载文件的后缀与源文件保持一致,可以设置缺省文件名:

<a href="test.png" download="joker">下载</a>

笔者曾遇到过一个问题,通过 <a> 标签触发跨域资源下载,代码与上述的 download 方法基本相同,只是在设置 download 属性的地方有点不同:

a.setAttribute(download, true)

结果在老版本的 Chrome 浏览器中出现了如下情况。

浅析HTML5中的download属性使用

下载文件名成了 true 。很明显,浏览器将 download 属性值读成了文件名。

经过分析,出现上述问题主要是因为:

1. 首先本不该将 download 设为 truedownloaddisabled 这种类型的属性值不同,它与文件名直接相关联。而且对于这种前后端响应式下载的方式, download 属性并不是必要的。

2. 在 Chrome 的早期版本不仅支持跨域资源的 download 属性下载,而且还可以通过 download 重置跨域资源的文件名,因此才会出现上述这种情况。

前后端配合完成文件下载的业务场景,一般是由后端设置响应头中的 Content-Disposition 信息来实现。

在 HTTP 场景中,Content-Disposition 第一个参数或者是 inline(默认值,表示回复中的消息体会以页面的一部分或者整个页面的形式展示),或者是 attachment(意味着消息体应该被下载到本地;大多数浏览器会呈现一个“保存为”的对话框,将 filename 的值预填为下载后的文件名)。

如果在响应头中设置了 Content-Disposition ,前端也在对应链接的 <a> 标签中添加了 download 属性,那么此时命名规则:

如果 HTTP 头中的 Content-Disposition 属性赋予了一个不同于此属性的文件名,HTTP 头属性优先于此属性。

经过测试发现,当 HTTP 头中 Content-Disposition 不为空时:

在 Chrome 浏览器中,不管 HTTP 头中 Content-Disposition 的第一个参数被设为 attachment 还是 inline ,只要设置了 filename, download 就无法重置文件名。相反,当 filename 为空时, download 属性值会被设为文件名。 在 FireFox 浏览器中,浏览器只会读取 Content-Disposition 的 filename 值,若是filename 为空,则取源文件名。此时 download 无论如何都无法重置文件名。

总结一下: 未在响应头设置 Content-Disposition 信息(例如一般直接定位资源的同源URL), download 属性可以重置文件名。若后端在 Content-Disposition 字段中已经设置了 filename,以 filename 值为准。

对于后端已经设定了文件名的情况下,如果仍然想要对文件名进行重置,该如何处理呢?

Blob: URL

关于 download 属性还有介绍:

尽管 HTTP URL 需要位于同一源中,但是可以使用 blob: URL 和 data: URL ,以方便用户下载使用 JavaScript 生成的内容(例如使用在线绘图 Web 应用程序创建的照片)。

Blob (Binary Large Object)即二进制大对象,这个我们并不陌生,一些数据库将Blob用来表示存储二进制文件的字段类型。File 接口也是基于 Blob,继承了 Blob 的功能并将其扩展使其支持用户系统上的文件。Blob 对象通过 Blob 构造函数来创建:

Blob(blobParts[, options])

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

如果需要实现一些简单的文本或 JS 字符串之类的文件下载,可以通过将文本信息转成 blob 二进制流,生成一个 Blob URL,配合 download 属性完成下载,代码如下。

const downloadText = (text, filename = '') {
  const a = document.createElement('a')
  a.download = filename
  const blob = new Blob([text], {type: 'text/plain'})  
  // text指需要下载的文本或字符串内容
  a.href = window.URL.createObjectURL(blob) 
  // 会生成一个类似blob:http://localhost:8080/d3958f5c-0777-0845-9dcf-2cb28783acaf 这样的URL字符串
  document.body.appendChild(a)  
  a.click()
  a.remove()
}

这种 Blob URL 与常见的 HTTP URL 有什么区别呢?

Blob URL / Object URL是一种伪协议,可以让Blob和File对象用作图像和二进制数据下载链接等URL源。

浏览器在内部通过 URL.createObjectURL() 创建一个对 Blob 或 File 对象的特殊引用,生成的 Blob URL 只能在浏览器本地的单个实例和同一会话中使用,并且这个 URL 对象会在页面退出的时候被浏览器释放。

因此 Blob URL 并不能指向一个服务器资源 ,你无法在其它页面中打开它。同时由于编码格式有所差别,Blob URL 比起 Data URLs 所占的空间资源更少,性能也更好。

Blob 为 Web 开发提供了一个非常有用的功能:创建 Blob URL。将二进制数据封装为 Blob 对象,然后使用 URL.createObjectURL() 生成 Blob URL,由于Blob URL本身就是一个同源URL,可以使用该 URL 配合 download 解决跨域资源的下载以及命名问题。

解决方案

通过 Blob 和 Fetch 可以解决跨域和文件命名的问题:使用 fetch 获取跨域资源返回一个blob 对象并生成一个 Blob URL,配合 <a> 标签的 download 属性触发下载,代码如下:

function download(href, filename = '')  {
  const a = document.createElement('a')
  a.download = filename
  a.href = href
  document.body.appendChild(a)  
  a.click()
  a.remove()
}

function downloadFile(url, filename='') {
  fetch(url, {
    headers: new Headers({
      Origin: location.origin,
    }),
    mode: 'cors',
  })
    .then(res => res.blob())
    .then(blob => {
      const blobUrl = window.URL.createObjectURL(blob)
      download(blobUrl, filename)
      window.URL.revokeObjectURL(blobUrl)
    })
}

此时再点击下载,可以正常的将跨域图片下载到本地了。

需注意的是跨域资源所在的服务器需要配置 Access-Control-Allow-Origin 信息,否则会得到一个大写的跨域报错。header 配置例如:

// 允许任何域名访问
header('Access-Control-Allow-Origin: *');

//指定域名访问
header('Access-Control-Allow-Origin: https://h5.ele.me');

目前这种方法还存在一些不足,例如浏览器会限制 Blob 数据大小不超过500M,在性能方面也会有所不足。

总结

目前前端有很多种下载方法, download 属性下载属于其中比较简单的一种,不过仔细考量其中的一些特性也能挖掘出很多有用的信息。 download 与浏览器特性紧密相关,目前该属性的兼容性也是一大问题,不过连微软官方都恳求用户不要再使用 IE ,相信以后 download 的兼容性问题会持续得到改善,应用前景也会越来越广阔。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

HTML / CSS 相关文章推荐
CSS3 特效范例整理
Aug 22 HTML / CSS
css3学习系列之移动属性详解
Jul 04 HTML / CSS
css3 实现滚动条美化效果的实例代码
Jan 06 HTML / CSS
CSS3只让背景图片旋转180度的实现示例
Mar 09 HTML / CSS
html5在移动端的屏幕适应问题示例探讨
Jun 15 HTML / CSS
HTML5本地存储之Web Storage详解
Jul 04 HTML / CSS
利用 Canvas实现绘画一个未闭合的带进度条的圆环
Jul 26 HTML / CSS
HTML5公共页面提取作为公用代码的方法
Jun 30 HTML / CSS
AmazeUI 折叠面板的实现代码
Aug 17 HTML / CSS
HTML5超文本标记语言的实现方法
Sep 24 HTML / CSS
基于HTML十秒做出淘宝页面
Oct 24 HTML / CSS
CSS SandBox应用场景及常见问题
Jun 25 HTML / CSS
手把手教你实现一个canvas智绘画板的方法
Mar 04 #HTML / CSS
详解h5页面在不同ios设备上的问题总结
Mar 01 #HTML / CSS
小程序瀑布流解决左右两边高度差距过大的问题
Feb 20 #HTML / CSS
h5网页水印SDK的实现代码示例
Feb 19 #HTML / CSS
canvas绘图按照contain或者cover方式适配并居中显示
Feb 18 #HTML / CSS
canvas 橡皮筋式线条绘图应用方法
Feb 13 #HTML / CSS
Canvas系列之滤镜效果
Feb 12 #HTML / CSS
You might like
让你的网站首页自动选择语言转跳
2006/12/06 PHP
用php实现像JSP,ASP里Application那样的全局变量
2007/01/12 PHP
用php来限制每个ip每天浏览页面数量的实现思路
2015/02/24 PHP
PHP获取数组最大值下标的方法
2015/05/12 PHP
yii添删改查实例
2015/11/16 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
详细分析PHP 命名空间(namespace)
2020/06/30 PHP
jQuery最佳实践完整篇
2011/08/20 Javascript
json原理分析及实例介绍
2012/11/29 Javascript
js设置cookie过期及清除浏览器对应名称的cookie
2013/10/24 Javascript
jQuery实现鼠标划过修改样式的方法
2015/04/14 Javascript
JavaScript中指定函数名称的相关方法
2015/06/04 Javascript
js+html5实现canvas绘制椭圆形图案的方法
2016/05/21 Javascript
Jquery on方法绑定事件后执行多次的解决方法
2016/06/02 Javascript
JS重载实现方法分析
2016/12/16 Javascript
vuejs2.0运用原生js实现简单的拖拽元素功能示例
2017/02/24 Javascript
JavaScript实现瀑布流图片效果
2017/06/30 Javascript
JS算法题之查找数字在数组中的索引位置
2019/05/15 Javascript
Python自动化测试Eclipse+Pydev 搭建开发环境
2016/08/15 Python
Python实现的微信公众号群发图片与文本消息功能实例详解
2017/06/30 Python
Python使用回溯法子集树模板解决爬楼梯问题示例
2017/09/08 Python
Python处理命令行参数模块optpars用法实例分析
2018/05/31 Python
Python3实现的简单工资管理系统示例
2019/03/12 Python
Python使用正则表达式分割字符串的实现方法
2019/07/16 Python
python实现随机加减法生成器
2020/02/24 Python
PyQt5事件处理之定时在控件上显示信息的代码
2020/03/25 Python
基于python判断字符串括号是否闭合{}[]()
2020/09/21 Python
python中如何使用虚拟环境
2020/10/14 Python
详解matplotlib中pyplot和面向对象两种绘图模式之间的关系
2021/01/22 Python
New Balance美国官网:运动鞋和健身服装
2017/04/11 全球购物
高档奢华时装在线目的地:FORWARD by elyse walker
2017/10/16 全球购物
土木工程专业推荐信
2014/02/19 职场文书
服务行业口号
2014/06/11 职场文书
postgresql无序uuid性能测试及对数据库的影响
2021/06/11 PostgreSQL
如何搭建 MySQL 高可用高性能集群
2021/06/21 MySQL
MySQL空间数据存储及函数
2021/09/25 MySQL