通过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几种形式的树结构菜单
May 10 Javascript
uploadify 3.0 详细使用说明
Jun 18 Javascript
Javascript算符的优先级介绍
Mar 20 Javascript
JavaScript语言核心数据类型和变量使用介绍
Aug 23 Javascript
JS使用oumousemove和oumouseout动态改变图片显示的方法
Mar 31 Javascript
每天一篇javascript学习小结(Function对象)
Nov 16 Javascript
JS实现的相册图片左右滚动完整实例
Nov 23 Javascript
jQuery is not defined 错误原因与解决方法小结
Mar 19 Javascript
详解实现一个通用的“划词高亮”在线笔记功能
Apr 23 Javascript
NProgress显示顶部进度条效果及使用详解
Sep 21 Javascript
javascript设计模式 ? 组合模式原理与应用实例分析
Apr 14 Javascript
Vite + React从零开始搭建一个开源组件库
Jun 25 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
开源SNS系统-ThinkSNS
2008/05/18 PHP
PHP 中检查或过滤IP地址的实现代码
2011/11/27 PHP
Laravel5.1框架路由分组用法实例分析
2020/01/04 PHP
JS数组去重与取重的示例代码
2014/01/24 Javascript
javascript中取前n天日期的两种方法分享
2014/01/26 Javascript
javascript 回到顶部效果的实现代码
2014/02/17 Javascript
js实现网页右上角滑出会自动消失大幅广告的方法
2015/02/27 Javascript
JS拖动鼠标画出方框实现鼠标选区的方法
2015/08/05 Javascript
JS中frameset框架弹出层实例代码
2016/04/01 Javascript
ionic js 复选框 与普通的 HTML 复选框到底有没区别
2016/06/06 Javascript
JavaScript实现类似淘宝的购物车效果
2017/03/16 Javascript
JavaScript结合HTML DOM实现联动菜单
2017/04/05 Javascript
JavaScript基于replace+正则实现ES6的字符串模版功能
2017/04/25 Javascript
JQuery Ajax执行跨域请求数据的解决方案
2018/12/10 jQuery
javascript json对象小技巧之键名作为变量用法分析
2019/11/11 Javascript
小程序点餐界面添加购物车左右摆动动画
2020/09/23 Javascript
Vue+Vant 图片上传加显示的案例
2020/11/03 Javascript
[54:56]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第三局
2016/03/06 DOTA
python实现自动登录人人网并访问最近来访者实例
2014/09/26 Python
python在Windows8下获取本机ip地址的方法
2015/03/14 Python
1 行 Python 代码快速实现 FTP 服务器
2018/01/25 Python
python opencv实现图片旋转矩形分割
2018/07/26 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
2018/12/18 Python
快速解决jupyter notebook启动需要密码的问题
2020/04/21 Python
python查找特定名称文件并按序号、文件名分行打印输出的方法
2020/04/24 Python
你需要学会的8个Python列表技巧
2020/06/24 Python
利用Python过滤相似文本的简单方法示例
2021/02/03 Python
HTML5通用接口详解
2016/06/12 HTML / CSS
Laura Mercier官网:彩妆大师罗拉玛斯亚的化妆品牌
2018/01/04 全球购物
*p++ 自增p 还是p所指向的变量
2016/07/16 面试题
内业资料员岗位职责
2014/01/04 职场文书
夜不归宿检讨书
2014/02/25 职场文书
《美丽的小路》教学反思
2014/02/26 职场文书
婚礼秀策划方案
2014/05/19 职场文书
教师党的群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
党员教师四风问题整改措施思想汇报
2014/10/08 职场文书