通过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 构建客户/服务分离的链接模型中Table分页代码效率初探
Jan 22 Javascript
js动态切换图片的方法
Jan 20 Javascript
React实现双向绑定示例代码
Sep 19 Javascript
微信小程序封装http访问网络库实例代码
May 24 Javascript
JavaScript基础之this详解
Jun 04 Javascript
JS设计模式之单例模式(一)
Sep 29 Javascript
详解Vue-cli webpack移动端自动化构建rem问题
Apr 07 Javascript
vue框架下部署上线后刷新报404问题的解决方案(推荐)
Apr 03 Javascript
layui的布局和表格的渲染以及动态生成表格的方法
Sep 18 Javascript
原生js+canvas实现验证码
Nov 29 Javascript
利用JavaScript模拟京东按键输入功能
Dec 01 Javascript
二维码条形码生成的JavaScript脚本库
Jul 07 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动态生成javascript文件的2个例子
2014/04/11 PHP
ThinkPHP框架设计及扩展详解
2014/11/25 PHP
javascript 变量作用域 代码分析
2009/06/26 Javascript
ExtJs扩展之GroupPropertyGrid代码
2010/03/05 Javascript
基于jQuery的图片大小自动适应实现代码
2010/11/17 Javascript
js自执行函数的几种不同写法的比较
2012/08/16 Javascript
简单实例处理url特殊符号&amp;处理(2种方法)
2013/04/02 Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
2013/04/11 Javascript
jQuery响应鼠标事件并隐藏与显示input默认值
2014/08/24 Javascript
JavaScript的RequireJS库入门指南
2015/07/01 Javascript
javascript实现方法调用与方法触发小结
2016/03/26 Javascript
提升页面加载速度的插件InstantClick
2017/09/12 Javascript
微信小程序使用gitee进行版本管理
2018/09/20 Javascript
常见的浏览器存储方式(cookie、localStorage、sessionStorage)
2019/05/07 Javascript
JS实现简单的表格增删
2020/01/16 Javascript
JS实现网站楼层导航效果代码实例
2020/06/16 Javascript
javaScript代码飘红报错看不懂?读完这篇文章再试试
2020/08/19 Javascript
[52:36]VGJ.S vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
python函数的5种参数详解
2017/02/24 Python
Pycharm学习教程(3) 代码运行调试
2017/05/03 Python
Python3+Requests+Excel完整接口自动化测试框架的实现
2019/10/11 Python
Keras - GPU ID 和显存占用设定步骤
2020/06/22 Python
Django封装交互接口代码
2020/07/12 Python
HTML5边玩边学(1)画布实现方法
2010/09/21 HTML / CSS
美国知名艺术画网站:Art.com
2017/02/09 全球购物
CHARLES & KEITH英国官网:新加坡时尚品牌
2018/07/04 全球购物
英国领先的酒杯和水晶玻璃器皿制造商:Dartington Crystal
2019/06/23 全球购物
学期自我评价
2014/01/27 职场文书
模范家庭事迹材料
2014/02/10 职场文书
党支部综合考察材料
2014/05/19 职场文书
保洁公司服务承诺书
2014/05/28 职场文书
师范学院毕业生求职信
2014/06/24 职场文书
党员志愿者活动总结
2014/06/26 职场文书
董事长助理工作职责范本
2014/07/01 职场文书
人口与计划生育责任书
2015/05/09 职场文书
JUnit5常用注解的使用
2021/07/02 Java/Android