通过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 相关文章推荐
Ext 表单布局实例代码
Apr 30 Javascript
JavaScript使用IEEE 标准进行二进制浮点运算产生莫名错误的解决方法
May 28 Javascript
jQuery.each使用详解
Jul 07 Javascript
JS实现可关闭的对联广告效果代码
Sep 14 Javascript
javascript实现网页中涉及的简易运动(改变宽高、透明度、位置)
Nov 29 Javascript
js实现文本框输入文字个数限制代码
Dec 25 Javascript
js按条件生成随机json:randomjson实现方法
Apr 07 Javascript
vue打包相关细节整理(小结)
Sep 28 Javascript
JQuery模拟实现网页中自定义鼠标右键菜单功能
Nov 14 jQuery
JavaScript实现的弹出遮罩层特效经典示例【基于jQuery】
Jul 10 jQuery
layui select 禁止点击的实现方法
Sep 05 Javascript
vue 项目软键盘回车触发搜索事件
Sep 09 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命令行用法
2015/02/04 PHP
修改WordPress中文章编辑器的样式的方法详解
2015/12/15 PHP
PHP 表单提交及处理表单数据详解及实例
2016/12/27 PHP
ext combox 下拉框不出现自动提示,自动选中的解决方法
2010/02/24 Javascript
来自qq的javascript面试题
2010/07/24 Javascript
js更优雅的兼容
2010/08/12 Javascript
jquery对table中各数据的增加、保存、删除操作示例
2014/05/14 Javascript
jQuery中Ajax的get、post等方法详解
2015/01/20 Javascript
jQuery实现炫酷的鼠标轨迹特效
2015/02/01 Javascript
Bootstrap按钮组件详解
2016/04/26 Javascript
js实现千分符和保留几位小数的简单实例
2016/08/01 Javascript
vue实现添加标签demo示例代码
2017/01/21 Javascript
详解EasyUi控件中的Datagrid
2017/08/23 Javascript
vue-cli2.9.3 详细教程
2018/04/23 Javascript
微信小程序实现横向增长表格的方法
2018/07/24 Javascript
JS隐藏号码中间4位代码实例
2019/04/09 Javascript
python方法生成txt标签文件的实例代码
2018/05/10 Python
使用Django启动命令行及执行脚本的方法
2018/05/29 Python
python将秒数转化为时间格式的实例
2018/09/16 Python
python3实现字符串操作的实例代码
2019/04/16 Python
python多线程并发实例及其优化
2019/06/27 Python
python3.7+selenium模拟淘宝登录功能的实现
2020/05/26 Python
Python3.9 beta2版本发布了,看看这7个新的PEP都是什么
2020/06/10 Python
python转化excel数字日期为标准日期操作
2020/07/14 Python
Python正则re模块使用步骤及原理解析
2020/08/18 Python
英国领先的办公用品供应商:Viking
2016/08/01 全球购物
波兰在线运动商店:YesSport
2020/07/23 全球购物
介绍一下游标
2012/01/10 面试题
优秀党员获奖感言
2014/02/18 职场文书
租房协议书范本
2014/04/09 职场文书
班级读书活动总结
2014/06/30 职场文书
纪检干部现实表现材料
2014/08/21 职场文书
房地产端午节活动方案
2014/08/24 职场文书
沙滩主题婚礼活动策划方案
2014/09/15 职场文书
社区党风廉政建设调研报告
2015/01/01 职场文书
redis实现排行榜功能
2021/05/24 Redis