通过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 迁移目录
Dec 18 Javascript
jquery 学习笔记一
Apr 07 Javascript
JavaScript中的数组操作介绍
Dec 30 Javascript
jQuery使用元素属性attr赋值详解
Feb 27 Javascript
JS键盘版计算器的制作方法
Dec 03 Javascript
bootstrap 表单验证使用方法
Jan 11 Javascript
ES10 特性的完整指南小结
Mar 04 Javascript
简单谈谈javascript高级特性
Sep 04 Javascript
Javascript实现html转pdf高清版(提高分辨率)
Feb 19 Javascript
vue实现购物车功能(商品分类)
Apr 20 Javascript
JavaScript如何实现监听键盘输入和鼠标监点击
Jul 20 Javascript
JS如何生成动态列表
Sep 22 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中使用正则表达式进行查找替换
2013/06/13 PHP
php简单的留言板与回复功能具体实现
2014/02/19 PHP
利用Laravel生成Gravatar头像地址的优雅方法
2017/12/30 PHP
PHP实现负载均衡的加权轮询方法分析
2018/08/22 PHP
PHP const定义常量及global定义全局常量实例解析
2020/05/28 PHP
javascript 学习之旅 (2)
2009/02/05 Javascript
动态创建样式表在各浏览器中的差异测试代码
2011/09/13 Javascript
JavaScript实现复制功能各浏览器支持情况实测
2013/07/18 Javascript
JavaScript把数组作为堆栈使用的方法
2015/03/20 Javascript
原生js实现class的添加和删除简单代码
2016/07/12 Javascript
关于js二维数组和多维数组的定义声明(详解)
2016/10/02 Javascript
jQuery之动画效果大全
2016/11/09 Javascript
Bootstrap 手风琴菜单的实现代码
2017/01/20 Javascript
详解微信小程序之scroll-view的flex布局问题
2019/01/16 Javascript
js中let能否完全替代IIFE
2019/06/15 Javascript
微信小程序全局变量的设置、使用、修改过程解析
2019/09/24 Javascript
Vue中使用matomo进行访问流量统计的实现
2019/11/05 Javascript
详解Vue的mixin策略
2020/11/19 Vue.js
JavaScript实现无限轮播效果
2020/11/19 Javascript
详解Python中的文本处理
2015/04/11 Python
Python中编写ORM框架的入门指引
2015/04/29 Python
在Mac OS上搭建Python的开发环境
2015/12/24 Python
详解Python中的相对导入和绝对导入
2017/01/06 Python
Python中enumerate()函数编写更Pythonic的循环
2018/03/06 Python
Python中的函数作用域
2018/05/07 Python
Python 判断图像是否读取成功的方法
2019/01/26 Python
Python range、enumerate和zip函数用法详解
2019/09/11 Python
Python+Redis实现布隆过滤器
2019/12/08 Python
django框架中ajax的使用及避开CSRF 验证的方式详解
2019/12/11 Python
Python打开文件、文件读写操作、with方式、文件常用函数实例分析
2020/01/07 Python
基于tensorflow __init__、build 和call的使用小结
2021/02/26 Python
2014年“向国旗敬礼”网上签名寄语活动方案
2014/09/27 职场文书
教师党的群众路线教育实践活动学习心得体会
2014/10/30 职场文书
毕业实习证明范本
2015/06/16 职场文书
python 如何用terminal输入参数
2021/05/25 Python
Lakehouse数据湖并发控制陷阱分析
2022/03/31 Oracle