JavaScrip如果基于url实现图片下载


Posted in Javascript onJuly 03, 2020

1.H5 download属性

function downFile(content, filename) {
  // 创建隐藏的可下载链接
  var eleLink = document.createElement('a');
  eleLink.download = filename;
  eleLink.style.display = 'none';
  // 字符内容转变成blob地址
  var blob = new Blob([content]);
  eleLink.href = URL.createObjectURL(blob);
  // 触发点击
  document.body.appendChild(eleLink);
  eleLink.click();
  // 然后移除
  document.body.removeChild(eleLink);
};

downFile(下载地址, 保存名称);

2.iframe方式

// if (typeof(download.iframe) == 'undefined') {
      //   var iframe = document.createElement('iframe');
      //   download.iframe = iframe;
      //   document.body.appendChild(download.iframe);
      // };
      // download.iframe.src = newdownloadUrl;
      // download.iframe.style.display = "none";

3.form方式

// var $eleForm = $("<form method='get'></form>");
      // $eleForm.attr("action", "https://codeload.github.com/douban/douban-client/legacy.zip/master");
      // $eleForm.attr("action", url);
      // $(document.body).append($eleForm);
      // $eleForm.submit();

downloadIamge(imgsrc, name) {//下载图片地址和图片名
 let image = new Image();
 // 解决跨域 Canvas 污染问题
 image.setAttribute("crossOrigin", "anonymous");
 image.onload = function() {
  let canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  let context = canvas.getContext("2d");
  context.drawImage(image, 0, 0, image.width, image.height);
  let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
  let a = document.createElement("a"); // 生成一个a元素
  let event = new MouseEvent("click"); // 创建一个单击事件
  a.download = name || "photo"; // 设置图片名称
  a.href = url; // 将生成的URL设置为a.href属性
  a.dispatchEvent(event); // 触发a的单击事件
 };
 image.src = imgsrc;
},

原理

我们先看看 download 的使用方法:

<a href="http://somehost/somefile.zip" rel="external nofollow" rel="external nofollow" download="filename.zip">Download file</a>

看看上面的代码,只要为 <a> 标签添加 download 属性,我们点击这个链接的时候就会自动下载文件了~

顺便说下,download 的属性值是可选的,它用来指定下载文件的文件名。像上面的例子中,我们下载到本地的文件名就会是 filename.zip 拉,如果不指定的话,它就会是 somefile.zip 这个名字拉!

看到这里,你可能会说,坑爹啊,这明明是用 HTML 5 的新特性来实现下载文件嘛,说好的用 JavaScript 下载文件呢?

事实上,用 JavaScript 来下载文件也是利用这一特性来实现的,我们的 JavaScript 代码不外乎就是:

  • 用 JavaScript 创建一个隐藏的 <a> 标签
  • 设置它的 href 属性
  • 设置它的 download 属性
  • 用 JavaScript 来触发这个它的 click 事件

翻译成 JavaScript 代码就是:

var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = 'what-you-want.txt';
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);

好拉,是不是看到有个陌生的东东呢?

window.URL

window.URL 里面有两个方法:

createObjectURL 用 blob 对象来创建一个 object URL(它是一个 DOMString),我们可以用这个 object URL 来表示某个 blob 对象,这个 object URL 可以用在 href 和 src 之类的属性上。

revokeObjectURL 释放由 createObjectURL 创建的 object URL,当该 object URL 不需要的时候,我们要主动调用这个方法来获取最佳性能和内存使用。

知道了这两个方法之后,我们再回去看看上面的例子就很容易理解了吧!只是用 blob 对象来创建一条 URL,然后让 <a> 标签引用该 URL,然后触发个点击事件,就可以下载文件了!

那么问题来了,blob 对象哪里来?

Blob 对象

Blob 全称是 Binary large object,它表示一个类文件对象,可以用它来表示一个文件。根据 MDN 上面的说法,File API 也是基于 blob 来实现的。

由于本文的主题是讲 JavaScript 下载文件,那我们构建 blob 的方式就是通过服务器返回的文件来创建 blob 拉!
而最简单的方式就是用 fetch API 了,我们可以整合上面的例子:

fetch('http://somehost/somefile.zip').then(res => res.blob().then(blob => {
var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = 'myfile.zip';
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}))

很简单对吧!

你可能会问,何必这么麻烦呢?直接写成下面这样不就好了:

<a href="http://somehost/somefile.zip" rel="external nofollow" rel="external nofollow" download="myfile.zip">Download file</a>
嗯,对于这种写法,我只能说,你做的太正确了!如果你要下载的是已经存在服务器上面的静态文件的话,那么写成这样是最方便的。浏览器会帮你处理整个下载过程,不需要你干涉。如果你用 blob 的方式来下载文件的话,会有下面这些限制的:

限制一:不同浏览器对 blob 对象有不同的限制

具体看看下面这个表格(出自 FileSaver.js):

Browser Constructs as Filenames Max Blob Size Dependencies
Firefox 20+ Blob Yes 800 MiB None
Firefox data: URI No n/a Blob.js
Chrome Blob Yes 500 MiB None
Chrome for Android Blob Yes 500 MiB None
Edge Blob Yes ? None
IE 10+ Blob Yes 600 MiB None
Opera 15+ Blob Yes 500 MiB None
Opera data: URI No n/a Blob.js
Safari 6.1+* Blob No ? None
Safari data: URI No n/a Blob.js

限制二:构建完 blob 对象后才会转换成文件

这一点限制对小文件(几十kb)可能没什么影响,但对稍微大一点的文件影响就很大了。试想,用户要下载一个 100mb 的文件,如

果他点击了下载按钮之后没看到下载提示的话,他肯定会继续按,等他按了几次之后还没看到下载提示时,他就会抱怨我们的网站,然后离开了。

然而事实上下载的的确确发生了,只是要等到下载完文件之后才能构建 blob 对象,再转化成文件。而且,用户再触发多几次下载就会造成一些资源上的浪费。

因此,如果是要下载大文件的话,还是推荐直接创建一个 <a> 标签拉~

写 html 也好,写 JavaScript 动态创建也好,用自己喜欢的方式去创建就好了。

为什么要用 JavaScript 下载文件

好拉,说了半天,其实我们一直说的都是:「不要用 JavaScript 下载文件拉,限制多多,又不好用,直接用 html 就好拉,简单方便又快捷」这个论调。

事实上也确实如此,但有些时候我们确实需要通过 JavaScript 来做一些预处理。

权限校验

有些时候,我们需要对下载做一些限制,最常见的就是权限校验了,如检查该用户是否有下载的权限,是否有高速下载的权限等等。这时候,我们可以利用 JavaScript 做一些预处理。如:

fetch('http://somehost/check-permission', options).then(res => {
if (res.code === 0) {
var a = document.createElement('a');
var url = res.data.url;
var filename = 'myfile.zip';
a.href = url;
a.download = filename;
a.click();
} else {
alert('You have no permission to download the file!');
}
});

在这个例子里面,我们没有用 blob 来构建 URL,而是通过后端服务器来计算出用户的下载链接,然后再利用之前提到的动态创建 <a> 标签的方式来实现下载,很简单吧!

动态文件

动态生成文件然后返回给客户端也是一个很常见的需求,譬如我们有时候需要做导出数据的功能,把数据库中的某些数据导出到 Excel 中,然后再返回客户端。

这时候我们就不能简单的指定 href 属性,因为对应的 URL 并不存在。

我们只能通过 JavaScript 对服务器发出一个请求,通知它去生成某个文件,然后把对应的 URL 返回给客户端。

有没有感觉这个过程和上面「权限校验」一节很像?肯定拉,因为我们只是对 URL 做了一些预处理而已嘛~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
当鼠标移动时出现特效的JQuery代码
Nov 08 Javascript
如何防止回车(enter)键提交表单
May 11 Javascript
javascript实现tab切换的两个实例
Nov 05 Javascript
js跨域请求数据的3种常用的方法
Dec 01 Javascript
JavaScript实现复制或剪切内容到剪贴板功能的方法
May 23 Javascript
Js动态设置rem来实现移动端字体的自适应代码
Oct 14 Javascript
Vue-router结合transition实现app前进后退动画切换效果的实例
Oct 11 Javascript
加载 vue 远程代码的组件实例详解
Nov 20 Javascript
webstorm中vue语法的支持详解
May 09 Javascript
JS数组进阶示例【数组的几种函数用法】
Jan 16 Javascript
解决vscode进行vue格式化,会自动补分号和双引号的问题
Oct 26 Javascript
js基于div丝滑实现贝塞尔曲线
Sep 23 Javascript
基于VUE实现判断设备是PC还是移动端
Jul 03 #Javascript
JavaScript如何判断对象有某属性
Jul 03 #Javascript
详解element-ui动态限定的日期范围选择器代码片段
Jul 03 #Javascript
JS常见错误(Error)及处理方案详解
Jul 02 #Javascript
vue过滤器实现日期格式化的案例分析
Jul 02 #Javascript
Vue使用预渲染代替SSR的方法
Jul 02 #Javascript
node运行js获得输出的三种方式示例详解
Jul 02 #Javascript
You might like
对javascript和select部件的结合运用
2006/10/09 PHP
PHP文件打开、关闭、写入的判断与执行代码
2011/05/24 PHP
PHP跨时区(UTC时间)应用解决方案
2013/01/11 PHP
PHP数组传递是值传递而非引用传递概念纠正
2013/01/31 PHP
PHP字符串的连接的简单实例
2013/12/30 PHP
php获得用户ip地址的比较不错的方法
2014/02/08 PHP
PHP+Ajax实现无刷新分页实例详解(附demo源码下载)
2016/04/07 PHP
php-fpm中max_children的配置
2019/03/15 PHP
php设计模式之观察者模式定义与用法经典示例
2019/09/19 PHP
利用json获取字符出现次数的代码
2012/03/22 Javascript
JS实现的驼峰式和连字符式转换功能分析
2016/12/21 Javascript
nodejs连接mysql数据库简单封装示例-mysql模块
2017/04/10 NodeJs
详解javascript中的babel到底是什么
2018/06/21 Javascript
JavaScript基础教程之如何实现一个简单的promise
2018/09/11 Javascript
解决Vue+Electron下Vuex的Dispatch没有效果问题
2019/05/20 Javascript
小程序使用分包的示例代码
2020/03/23 Javascript
查找Vue中下标的操作(some和findindex)
2020/08/12 Javascript
JavaScript 事件代理需要注意的地方
2020/09/08 Javascript
python读取和保存视频文件
2018/04/16 Python
Python实现的多进程和多线程功能示例
2018/05/29 Python
python3读取excel文件只提取某些行某些列的值方法
2018/07/10 Python
Python爬虫设置ip代理过程解析
2020/07/20 Python
python中使用np.delete()的实例方法
2021/02/01 Python
Python3爬虫ChromeDriver的安装实例
2021/02/06 Python
无需JS和jQuery代码实现CSS3鼠标浮动放大图片
2016/11/21 HTML / CSS
物流毕业生个人的自我评价
2014/02/13 职场文书
感恩节活动策划方案
2014/05/16 职场文书
国际商务专业毕业生自我鉴定2014
2014/09/27 职场文书
安全生产月宣传标语
2014/10/06 职场文书
大学生党员自我批评思想汇报
2014/10/10 职场文书
2014年教师工作总结
2014/11/10 职场文书
保研专家推荐信范文
2015/03/25 职场文书
爱岗敬业先进典型事迹材料(2016推荐版)
2016/02/26 职场文书
利用python Pandas实现批量拆分Excel与合并Excel
2021/05/23 Python
虚拟机linux端mysql数据库无法远程访问的解决办法
2021/05/26 MySQL
Python内置包对JSON文件数据进行编码和解码
2022/04/12 Python