通过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 解决表单仍然提交即使监听处理函数返回false
Mar 14 Javascript
jQuery 1.5 源码解读 面向中高阶JSER
Apr 05 Javascript
关于jQuery新的事件绑定机制on()的使用技巧
Apr 26 Javascript
使用JQUERY进行后台页面布局控制DIV实现左右式
Jan 07 Javascript
jQuery实现的网页竖向菜单效果代码
Aug 26 Javascript
关于js函数解释(包括内嵌,对象等)
Nov 20 Javascript
javaScript语法总结
Nov 25 Javascript
Angular.Js之Scope作用域的学习教程
Apr 27 Javascript
Angular 4依赖注入学习教程之组件服务注入(二)
Jun 04 Javascript
基于bootstrap写的一点localStorage本地储存
Nov 21 Javascript
使用vue-router beforEach实现判断用户登录跳转路由筛选功能
Jun 25 Javascript
Vue 前端实现登陆拦截及axios 拦截器的使用
Jul 17 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
Search File Contents PHP 搜索目录文本内容的代码
2010/02/21 PHP
Thinkphp的volist标签嵌套循环使用教程
2014/07/08 PHP
php中使用key,value,current,next和prev函数遍历数组的方法
2015/03/17 PHP
Extjs TimeField 显示正常时间格式的代码
2011/06/28 Javascript
js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
2012/05/16 Javascript
JQuery入门——事件切换之toggle()方法应用介绍
2013/02/05 Javascript
P3P Header解决Cookie跨域的问题
2013/03/12 Javascript
图片轮换效果实现代码(点击按钮停止执行)
2013/04/12 Javascript
Js日期选择器并自动加入到输入框中示例代码
2013/08/02 Javascript
javascript模拟实现C# String.format函数功能代码
2013/11/25 Javascript
Jquery解析json数据详解
2013/12/26 Javascript
JavaScript实现的简单烟花特效代码
2015/10/20 Javascript
jquery表单验证插件formValidator使用方法
2016/04/01 Javascript
javascript中Number的方法小结
2016/11/21 Javascript
DOM 事件的深入浅出(一)
2016/12/05 Javascript
vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据
2020/10/26 Javascript
NodeJS配置HTTPS服务实例分享
2017/02/19 NodeJs
详解node-ccap模块生成captcha验证码
2017/07/01 Javascript
Angular实现预加载延迟模块的示例
2017/10/12 Javascript
详解vue axios二次封装
2018/07/22 Javascript
Angular resolve基础用法详解
2018/10/03 Javascript
vue 双向数据绑定的实现学习之监听器的实现方法
2018/11/30 Javascript
vue表单验证你真的会了吗?vue表单验证(form)validate
2019/04/07 Javascript
layui表单提交到后台自动封装到实体类的方法
2019/09/12 Javascript
Python 流程控制实例代码
2009/09/25 Python
Python读写Redis数据库操作示例
2014/03/18 Python
Python 的 Socket 编程
2015/03/24 Python
python去除文件中重复的行实例
2018/06/29 Python
Python使用logging模块实现打印log到指定文件的方法
2018/09/05 Python
Python 等分切分数据及规则命名的实例代码
2019/08/16 Python
python异步编程 使用yield from过程解析
2019/09/25 Python
python tornado修改log输出方式
2019/11/18 Python
使用Python脚本从文件读取数据代码实例
2020/01/19 Python
标准毕业生自荐信
2014/06/24 职场文书
员工开除通知书
2015/04/25 职场文书
Python中for后接else的语法使用
2021/05/18 Python