通过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 相关文章推荐
javascript下string.format函数补充
Aug 24 Javascript
定时器(setTimeout/setInterval)调用带参函数失效解决方法
Mar 26 Javascript
屏蔽IE弹出&quot;您查看的网页正在试图关闭窗口,是否关闭此窗口&quot;的方法
Dec 31 Javascript
JS倒计时代码汇总
Nov 25 Javascript
JS实现仿腾讯微博无刷新删除微博效果代码
Oct 16 Javascript
js实现商品抛物线加入购物车特效
Nov 18 Javascript
jQuery 插件封装的方法
Nov 16 Javascript
JavaScript中清空数组的方法总结
Dec 02 Javascript
JS实现动画兼容性的transition和transform实例分析
Dec 13 Javascript
基于Vue实现页面切换左右滑动效果
Jun 29 Javascript
微信小程序实现笑脸评分功能
Nov 03 Javascript
使用canvas实现一个vue弹幕组件功能
Nov 30 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
那些年一起学习的PHP(二)
2012/03/21 PHP
跟着JQuery API学Jquery 之三 筛选
2010/04/09 Javascript
ExtJs使用总结(非常详细)
2012/03/22 Javascript
javascript之typeof、instanceof操作符使用探讨
2013/05/19 Javascript
动态加载脚本提升javascript性能
2014/02/24 Javascript
node.js中的fs.readlink方法使用说明
2014/12/17 Javascript
基于javascript实现彩票随机数生成(升级版)
2020/04/17 Javascript
Nodejs中的this详解
2016/03/26 NodeJs
浅析Bootstrip的select控件绑定数据的问题
2016/05/10 Javascript
动态设置form表单的action属性的值的简单方法
2016/05/25 Javascript
AngularJS中指令的四种基本形式实例分析
2016/11/22 Javascript
高效的jQuery代码编写技巧总结
2017/02/22 Javascript
JavaScript创建对象的七种方式(推荐)
2017/06/26 Javascript
Nuxt.js实战详解
2018/01/18 Javascript
JS实现监控微信小程序的原理
2018/06/15 Javascript
vue移动端城市三级联动组件使用详解
2019/07/26 Javascript
基于vue.js实现购物车
2020/01/15 Javascript
vue+render+jsx实现可编辑动态多级表头table的实例代码
2020/04/01 Javascript
[41:08]2014 DOTA2国际邀请赛中国区预选赛 HGT VS NE
2014/05/22 DOTA
python中字符串比较使用is、==和cmp()总结
2018/03/18 Python
python中csv文件的若干读写方法小结
2018/07/04 Python
对python数据切割归并算法的实例讲解
2018/12/12 Python
python dict 相同key 合并value的实例
2019/01/21 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
2019/08/05 Python
python安装virtualenv虚拟环境步骤图文详解
2019/09/18 Python
css3制作彩色边线3d立体按钮的示例(css3按钮)
2014/05/06 HTML / CSS
HTML5拖放功能_动力节点Java学院整理
2017/07/13 HTML / CSS
意大利制造的西装、衬衫和针对男士量身定制的服装:Lanieri
2018/04/08 全球购物
2015年事业单位办公室文员工作总结
2015/04/24 职场文书
患者身份识别制度
2015/08/06 职场文书
mysql left join快速转inner join的过程
2021/06/30 MySQL
Mysql实现简易版搜索引擎的示例代码
2021/08/30 MySQL
Mac电脑OS系统下安装Nginx的详细教程
2022/04/14 Servers
Go微服务项目配置文件的定义和读取示例详解
2022/06/21 Golang
Windows server 2012 NTP时间同步的实现
2022/06/25 Servers
Python中的socket网络模块介绍
2022/07/23 Python