通过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源码分析-01总体架构分析
Nov 14 Javascript
jQuery实现设置、移除文本框默认值功能
Jan 13 Javascript
js树插件zTree获取所有选中节点数据的方法
Jan 28 Javascript
简介JavaScript中fixed()方法的使用
Jun 08 Javascript
jquery实现简单的瀑布流布局
Dec 11 Javascript
Bootstrap BootstrapDialog使用详解
Feb 17 Javascript
VueRouter导航守卫用法详解
Dec 25 Javascript
vue实现移动端轻量日期组件不依赖第三方库的方法
Apr 28 Javascript
微信小程序 image组件遇到的问题
May 28 Javascript
微信公众号获取用户地理位置并列出附近的门店的示例代码
Jul 25 Javascript
ES6学习笔记之字符串、数组、对象、函数新增知识点实例分析
Jan 22 Javascript
浅谈JavaScript中this的指向问题
Jul 28 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开发的9条建议
2015/07/27 PHP
yii添删改查实例
2015/11/16 PHP
thinkphp3.x中session方法的用法分析
2016/05/20 PHP
JQuery 表格操作(交替显示、拖动表格行、选择行等)
2009/07/29 Javascript
基于jquery的无刷新分页技术
2011/06/11 Javascript
使用Json比用string返回数据更友好,也更面向对象一些
2011/09/13 Javascript
jquery ajax return没有返回值的解决方法
2011/10/20 Javascript
js实现简单的星级选择器提交效果适用于评论等
2013/10/18 Javascript
JavaScript 32位整型无符号操作示例
2013/12/08 Javascript
网络传输协议(http协议)
2016/11/18 Javascript
详解jquery validate实现表单验证 (正则表达式)
2017/01/18 Javascript
详解Vue方法与事件
2017/03/09 Javascript
详解如何在angular2中获取节点
2017/11/23 Javascript
基于JSONP原理解析(推荐)
2017/12/04 Javascript
浅谈Angular单元测试总结
2019/03/22 Javascript
vue2之简易的pc端短信验证码的问题及处理方法
2019/06/03 Javascript
layui button 按钮弹出提示窗口,确定才进行的方法
2019/09/06 Javascript
从零学Python之入门(四)运算
2014/05/27 Python
Python中运算符&quot;==&quot;和&quot;is&quot;的详解
2016/10/08 Python
Django框架中间件(Middleware)用法实例分析
2019/05/24 Python
Python读取JSON数据操作实例解析
2020/05/18 Python
python进度条显示之tqmd模块
2020/08/22 Python
python实现人性化显示金额数字实例详解
2020/09/25 Python
简单介绍HTML5中audio标签的使用
2015/09/24 HTML / CSS
史蒂夫·马登加拿大官网:Steve Madden加拿大
2017/11/18 全球购物
奇怪的鱼:Weird Fish
2018/03/18 全球购物
波兰家居和花园家具专家:4Home
2019/05/26 全球购物
医学院学生求职简历的自我评价
2013/10/24 职场文书
高一自我鉴定
2013/12/17 职场文书
公司活动策划方案
2014/01/13 职场文书
垃圾桶标语
2014/06/24 职场文书
领导班子三严三实心得体会
2014/10/13 职场文书
涨工资申请书应该怎么写?
2019/07/08 职场文书
python读取pdf格式文档的实现代码
2021/04/01 Python
在Spring-Boot中如何使用@Value注解注入集合类
2021/08/02 Java/Android
i5-10400f处理相当于i7多少水平
2022/04/19 数码科技