通过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 相关文章推荐
JS 文件本身编码转换 图文教程
Oct 12 Javascript
JS控制文本框textarea输入字数限制的方法
Jun 17 Javascript
Extjs Label的 fieldLabel和html属性值对齐的方法
Jun 15 Javascript
JavaScript的Backbone.js框架的一些使用建议整理
Feb 14 Javascript
AngularJs表单校验功能实例代码
Feb 09 Javascript
详解vue + vuex + directives实现权限按钮的思路
Oct 24 Javascript
vue 点击按钮增加一行的方法
Sep 07 Javascript
微信小程序实现左右列表联动
May 19 Javascript
Vue.js 中的实用工具方法【推荐】
Jul 04 Javascript
vuex存储复杂参数(如对象数组等)刷新数据丢失的解决方法
Nov 05 Javascript
javascript 原型与原型链的理解及实例分析
Nov 23 Javascript
js实现简易点击切换显示或隐藏
Nov 29 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
基于mysql的bbs设计(三)
2006/10/09 PHP
PHP中Header使用的HTTP协议及常用方法小结
2014/11/04 PHP
phpstorm配置Xdebug进行调试PHP教程
2014/12/01 PHP
Laravel框架控制器的middleware中间件用法分析
2019/09/30 PHP
Laravel 微信小程序后端搭建步骤详解
2019/11/26 PHP
脚本吧 - 幻宇工作室用到js,超强推荐share.js
2006/12/23 Javascript
23个Javascript弹出窗口特效整理
2011/02/25 Javascript
读jQuery之八 包装事件对象
2011/06/21 Javascript
js 利用className得到对象的实现代码
2011/11/15 Javascript
通过百度地图获取公交线路的站点坐标的js代码
2012/05/11 Javascript
浅谈JavaScript中的this指针和引用知识
2016/08/05 Javascript
jQuery实现获取table中鼠标click点击位置行号与列号的方法
2017/10/09 jQuery
angular1配合gulp和bower的使用教程
2018/01/19 Javascript
每个 JavaScript 工程师都应懂的33个概念
2018/10/22 Javascript
JS数组求和的常用方法总结【5种方法】
2019/01/14 Javascript
Element中的Cascader(级联列表)动态加载省\市\区数据的方法
2019/03/27 Javascript
微信小程序前端自定义分享的实现方法
2019/06/13 Javascript
JavaScript中判断为整数的多种方式及保留两位小数的方法
2019/09/09 Javascript
[01:05:36]VP vs TNC Supermajor小组赛B组 BO3 第二场 6.2
2018/06/03 DOTA
详解Python异常处理中的Finally else的功能
2017/12/29 Python
Python实现的自定义多线程多进程类示例
2018/03/23 Python
利用Python如何将数据写到CSV文件中
2018/06/05 Python
浅谈Python在pycharm中的调试(debug)
2018/11/29 Python
python ChainMap的使用和说明详解
2019/06/11 Python
python的几种矩阵相乘的公式详解
2019/07/10 Python
pygame实现飞机大战
2020/03/11 Python
python实现简单俄罗斯方块
2020/03/13 Python
Python3 中sorted() 函数的用法
2020/03/24 Python
PyQt5实现登录页面
2020/05/30 Python
Python趣味实例,实现一个简单的抽奖刮刮卡
2020/07/18 Python
Python内存泄漏和内存溢出的解决方案
2020/09/26 Python
python UIAutomator2使用超详细教程
2021/02/19 Python
学校欢迎标语
2014/06/18 职场文书
体育个人工作总结
2015/02/09 职场文书
写好求职信的技巧解密
2019/05/14 职场文书
python 命令行传参方法总结
2021/05/25 Python