通过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 相关文章推荐
Auntion-TableSort国人写的一个javascript表格排序的东西
Nov 12 Javascript
JavaScript代码复用模式实例分析
Dec 02 Javascript
windows8.1+iis8.5下安装node.js开发环境
Dec 12 Javascript
jQuery中:enabled选择器用法实例
Jan 04 Javascript
JavaScript实现点击单选按钮改变输入框中文本域内容的方法
Aug 12 Javascript
BootStrap入门教程(一)之可视化布局
Sep 19 Javascript
Bootstrap CSS组件之分页(pagination)和翻页(pager)
Dec 17 Javascript
Jquery Easyui菜单组件Menu使用详解(15)
Dec 18 Javascript
图片懒加载插件实例分享(含解析)
Jan 09 Javascript
jQuery Ajax async=&gt;false异步改为同步时,解决导致浏览器假死的问题
Jul 22 jQuery
layui-table对返回的数据进行转变显示的实例
Sep 04 Javascript
原生js中运算符及流程控制示例详解
Jan 05 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 similar_text 字符串的相似性比较函数
2010/05/26 PHP
php写的AES加密解密类分享
2014/06/20 PHP
YII CLinkPager分页类扩展增加显示共多少页
2016/01/29 PHP
twig模板常用语句实例小结
2016/02/04 PHP
php常用字符串长度函数strlen()与mb_strlen()用法实例分析
2019/06/25 PHP
判断是否输入完毕再激活提交按钮
2006/06/26 Javascript
javascript数组去重的方法汇总
2015/04/14 Javascript
javascript实现状态栏文字首尾相接循环滚动的方法
2015/07/22 Javascript
JS中跨页面调用变量和函数的方法(例如a.js 和 b.js中互相调用)
2016/11/01 Javascript
使用InstantClick.js让页面提前加载200ms
2017/09/12 Javascript
vue中引用阿里字体图标的方法
2018/02/10 Javascript
vue组件详解之使用slot分发内容
2018/04/09 Javascript
JavaScript实现的DOM树遍历方法详解【二叉DOM树、多叉DOM树】
2018/05/07 Javascript
微信小程序input框中加入小图标的实现方法
2018/06/19 Javascript
JS使用对象的defineProperty进行变量监控操作示例
2019/02/02 Javascript
Vuejs通过拖动改变元素宽度实现自适应
2020/09/02 Javascript
Python实现扫描局域网活动ip(扫描在线电脑)
2015/04/28 Python
深入解析Python中的线程同步方法
2016/06/14 Python
Python实现类的创建与使用方法示例
2017/07/25 Python
python使用tkinter实现简单计算器
2018/01/30 Python
解决pip install的时候报错timed out的问题
2018/06/12 Python
python正则表达式匹配[]中间为任意字符的实例
2018/12/25 Python
python http基本验证方法
2018/12/26 Python
在Python 字典中一键对应多个值的实例
2019/02/03 Python
让Python脚本暂停执行的几种方法(小结)
2019/07/11 Python
Python StringIO如何在内存中读写str
2020/01/07 Python
Python selenium如何打包静态网页并下载
2020/08/12 Python
超级实用的8个Python列表技巧
2020/08/24 Python
浅析css3中matrix函数的使用
2016/06/06 HTML / CSS
销售经理工作职责范文
2013/12/03 职场文书
借款协议书
2014/09/16 职场文书
2014年药房工作总结
2014/11/22 职场文书
会计求职自荐信
2015/03/26 职场文书
团拜会主持词
2015/07/04 职场文书
《圆明园的毁灭》教学反思
2016/02/16 职场文书
奇妙的 CSS shapes(CSS图形)
2021/04/05 HTML / CSS