通过JavaScript下载文件到本地的方法(单文件)


Posted in Javascript onMarch 17, 2019

最近在做一个文件下载的功能,这里把做的过程中用的技术和坑简要总结下。

1. 单文件下载(a标签)

同源单文件

针对单文件的情况下,同源的文件,可以通过 < a> 标签的 download 属性下载文件

const elt = document.createElement('a');
 elt.setAttribute('href', url);
 elt.setAttribute('download', 'file.png');
 elt.style.display = 'none';
 document.body.appendChild(elt);
 elt.click();
 document.body.removeChild(elt);

但是这个方案并不适用于非同源的资源,此时它相当于普通的超链接,点击会跳转到资源页面,而不是下载。

非同源图片

如果不存在CORS问题, 可以借助Blob实现下载(构造xhr请求文件地址, 以Blob的形式接收Response):

function downloadWithBlob(url) {
 fetch(url).then(res => res.blob().then(blob => {
  var a = document.createElement('a');
  var url = window.URL.createObjectURL(blob);
  var filename = 'file.png';
  a.href = url;
  a.download = filename;
  a.click();
  window.URL.revokeObjectURL(url);
 }));
}

如果存在CORS问题,可以考虑使用 canvas 将图片转换成 base64 编码之后再通过 标签的 download 属性下载

function downloadPic(url) {
 const img = new Image;
 const canvas = document.createElement('canvas');
 const ctx = canvas.getContext('2d');
 img.onload = function() {
  canvas.width = this.width;
  canvas.height = this.height;
  ctx.drawImage(this, 0, 0);

  const elt = document.createElement('a');
  elt.setAttribute('href', canvas.toDataURL('image/png'));
  elt.setAttribute('download', 'file.png');
  elt.style.display = 'none';
  document.body.appendChild(elt);
  elt.click();
  document.body.removeChild(elt);
 };
 img.crossOrigin = 'anonymous';
 img.src = url;
}

2. 单文件下载(iframe)

iframe方式是在页面内隐藏iframe, 然后将下载地址加载到iframe中, 从而触发浏览器的下载行为

const iframe = document.createElement('iframe');
 iframe.src = url;
 iframe.style.display = 'none';
 document.body.appendChild(iframe);

但是这里发现,即使是同域的图片,也无法完成下载,这是为啥呢?

这里就有个上面的a链接下载没有提到的问题:什么样的链接才能触发浏览器的下载:

url如何触发浏览器自动下载

一个url能否触发浏览器自动下载,主要看该请求响应头response header是否满足,一般是看Content-Disposition和Content-Type这两个消息头:

  • response header中指定了Content-Disposition为attachment,它表示让浏览器把消息体以附件的形式下载并保存到本地 (一般还会指定filename, 下载的文件名默认就是filename)
  • response header中指定了Content-Type 为 application/octet-stream(无类型) 或 application/zip(zip包时)等等。(其中 application/octet-stream表示http response为二进制流(没指定明确的type), 用在未知的应用程序文件,浏览器一般不会自动执行或询问执行。浏览器会像对待 设置了HTTP头Content-Disposition 值为 attachment 的文件一样来对待这类文件)

只要url满足上述触发的要求,那么都可以通过iframe的形式来下载

3. 代理服务处理下载

如果后端自己也能控制的话,或者后端能配合的话,可以写一个代理服务,在后端去请求文件数据,然后设置好相应的response header, 然后前端请求代理服务来做下载。

前端(假设代理服务接口是http://exampale.com/download):

const downloadUrl = 'http://exampale.com/download?url=' + encodeURIComponent(url) + '&name=xxx';
 const elt = document.createElement('a');
 elt.setAttribute('href', downloadUrl);
 elt.setAttribute('download', 'file.png');
 ...

后端

const url = decodeURIComponent(req.query.url);
http.get(url, (response) => {
 res.setHeader('Content-disposition', 'attachment;filename=' + req.query.name);
 res.setHeader('Content-type', 'application/octet-stream');
 response.pipe(res);
});

单文件的处理先写到这里,多文件的下载下篇在写。

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

Javascript 相关文章推荐
WordPress JQuery处理沙发头像
Jun 22 Javascript
onkeypress字符按键兼容所有浏览器使用介绍
Apr 24 Javascript
解析offsetHeight,clientHeight,scrollHeight之间的区别
Nov 20 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
Oct 15 Javascript
Bootstrap中CSS的使用方法
Feb 17 Javascript
jquery dataview数据视图插件使用方法
Dec 23 Javascript
node.js实现登录注册页面
Apr 08 Javascript
JavaScript实现三级联动菜单效果
Aug 16 Javascript
详解Vuex管理登录状态
Nov 13 Javascript
vue ssr 指南详读
Jun 29 Javascript
小程序如何支持使用 async/await详解
Sep 12 Javascript
JS字符串补全方法padStart()和padEnd()
May 27 Javascript
微信小程序登录session的使用
Mar 17 #Javascript
Javascript读写cookie的实例源码
Mar 16 #Javascript
vue自定义键盘信息、监听数据变化的方法示例【基于vm.$watch】
Mar 16 #Javascript
vue自定义指令用法经典实例小结
Mar 16 #Javascript
简单易扩展可控性强的Jquery转盘抽奖程序
Mar 16 #jQuery
基于vue通用表单解决方案的思考与分析
Mar 16 #Javascript
vue+php实现的微博留言功能示例
Mar 16 #Javascript
You might like
函数中使用require_once问题深入探讨 优雅的配置文件定义方法推荐
2014/07/02 PHP
PHP手机号中间四位用星号*代替显示的实例
2017/06/02 PHP
浅谈Laravel中的一个后期静态绑定
2017/08/11 PHP
PHP实现文件上传操作和封装
2020/03/04 PHP
jquery键盘事件使用介绍
2011/11/01 Javascript
基于jQuery的获取标签名的代码
2012/07/16 Javascript
JSON无限折叠菜单编写实例
2013/12/16 Javascript
JavaScript的null和undefined区别示例介绍
2014/09/15 Javascript
JavaScript组件焦点与页内锚点间传值的方法
2015/02/02 Javascript
javascript实现超炫的向上滑行菜单实例
2015/08/03 Javascript
jquery实现简洁文件上传表单样式
2015/11/02 Javascript
微信小程序 页面跳转传参详解
2016/10/28 Javascript
js获取ip和地区
2017/03/10 Javascript
jQuery选择器_动力节点Java学院整理
2017/07/05 jQuery
基于Bootstrap table组件实现多层表头的实例代码
2017/09/07 Javascript
浅谈Vue.js中ref ($refs)用法举例总结
2017/12/19 Javascript
实例解析ES6 Proxy使用场景介绍
2018/01/08 Javascript
js实现转动骰子模型
2019/10/24 Javascript
python开发之tkinter实现图形随鼠标移动的方法
2015/11/11 Python
Python在图片中添加文字的两种方法
2017/04/29 Python
python分析作业提交情况
2017/11/22 Python
浅析python3中的os.path.dirname(__file__)的使用
2018/08/30 Python
python能自学吗
2020/06/18 Python
浅析python中的del用法
2020/09/02 Python
使用CSS实现弹性视频html5案例实践
2012/12/26 HTML / CSS
新锐科技Java程序员面试题
2016/07/25 面试题
房地产融资计划书
2014/01/10 职场文书
高中考试作弊检讨书
2014/01/14 职场文书
《黄山奇石》教学反思
2014/04/19 职场文书
节约用水的口号
2014/06/20 职场文书
我的中国梦演讲稿300字
2014/08/19 职场文书
预备党员群众路线教育实践活动思想汇报2014
2014/10/25 职场文书
计划生育工作汇报
2014/10/28 职场文书
Python如何解决secure_filename对中文不支持问题
2021/07/16 Python
Python 中的 copy()和deepcopy()
2021/11/07 Python
Redis高可用集群redis-cluster详解
2022/03/20 Redis