通过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 相关文章推荐
jQuery 获取对象 根据属性、内容匹配, 还有表单元素匹配
May 31 Javascript
js读取配置文件自写
Feb 11 Javascript
js类型转换与引用类型详解(Boolean_Number_String)
Mar 07 Javascript
javascript的创建多行字符串的7种方法
Apr 29 Javascript
JavaScript中提前声明变量或函数例子
Nov 12 Javascript
jquery判断input值不为空的方法
Jun 05 Javascript
javascript之Boolean类型对象
Jun 07 Javascript
jquery表单插件Autotab使用方法详解
Jun 24 Javascript
简单实现IONIC购物车功能
Jan 10 Javascript
用vue构建多页面应用的示例代码
Sep 20 Javascript
js中json对象和字符串的理解及相互转化操作实现方法
Sep 22 Javascript
JavaScript动态生成表格的示例
Nov 02 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
BBS(php &amp; mysql)完整版(八)
2006/10/09 PHP
php实现文件下载代码分享
2014/08/19 PHP
php使用ffmpeg向视频中添加文字字幕的实现方法
2016/05/23 PHP
PHP实现的随机IP函数【国内IP段】
2016/07/20 PHP
如何打开php的gd2库
2017/02/09 PHP
yii2局部关闭(开启)csrf的验证的实例代码
2017/07/10 PHP
PHP高并发和大流量解决方案整理
2021/03/09 PHP
js切换div css注意的细节
2012/12/10 Javascript
Node.js开发指南中的简单实例(mysql版)
2013/09/17 Javascript
jquery easyui中treegrid用法的简单实例
2014/02/18 Javascript
jQuery 获取兄弟元素的几种不错方法
2014/05/23 Javascript
javascript中Math.random()使用详解
2015/04/15 Javascript
跟我学习JScript的Bug与内存管理
2015/11/18 Javascript
ES6中的数组扩展方法
2016/08/26 Javascript
js select下拉联动 更具级联性!
2020/04/17 Javascript
bootstrap 模态框(modal)实现水平垂直居中显示
2017/01/23 Javascript
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
2018/11/05 Javascript
微信小程序实现左右列表联动
2020/05/19 Javascript
python实现随机森林random forest的原理及方法
2017/12/21 Python
matplotlib绘制动画代码示例
2018/01/02 Python
Python之csv文件从MySQL数据库导入导出的方法
2018/06/21 Python
解决python打不开文件(文件不存在)的问题
2019/02/18 Python
使用Python制作表情包实现换脸功能
2019/07/19 Python
解决Django Static内容不能加载显示的问题
2019/07/28 Python
pycharm2020.1.2永久破解激活教程,实测有效
2020/10/29 Python
Django怎么在admin后台注册数据库表
2020/11/14 Python
英国最大的电子零件及配件零售商:Partmaster
2017/04/24 全球购物
动漫设计与制作专业推荐信
2014/07/07 职场文书
2015年国际护士节演讲稿
2015/03/18 职场文书
2015年纪委工作总结
2015/05/13 职场文书
优秀学生主要事迹怎么写
2015/11/04 职场文书
党员学习中国梦心得体会
2016/01/05 职场文书
XX部保密工作制度范本
2019/08/27 职场文书
JavaScript实现班级抽签小程序
2021/05/19 Javascript
python 中的@运算符使用
2021/05/26 Python
5种方法告诉你如何使JavaScript 代码库更干净
2021/09/15 Javascript