使用 JavaScript 创建并下载文件

前端很多项目中,都有文件下载的需求,特别是JS生成文件内容,然后让浏览器执行下载操作(例如在线图片编辑、在线代码编辑、iPresst等)。

但受限于浏览器,很多情况下我们都只能给出个链接,让用户点击打开-》另存为。如下面这个链接:

<a href=”file.js”>file.js</a>

用户点击这个链接的时候,浏览器会打开并显示链接指向的文件内容,显然,这并没有实现我们的需求。

HTML5中给a标签增加了一个download属性,只要有这个属性,点击这个链接时浏览器就不在打开链接指向的文件,而是改为下载(目前只有chrome、firefox和opera支持)。

022016RkC

下载时会直接使用链接的名字来作为文件名,但是是可以改的,只要给download加上想要的文件名即可,如:download=“not-a-file.js”。

022017sna

Not enough!

但是这样还不够,以上的方法只适合用在文件是在服务器上的情况。如果在浏览器端js生成的内容,想让浏览器进行下载要如何办到呢?

其实还是有办法办到的,相信很多人都多少听过了DataURI这个词,比较常见的就是图片的src,如:

<img src=”data:image/gif;base64,R0lGOXXXXX">

DataURI的解释可以移步这里,本人就不在解释了。

那么,现在要将js生成的内容进行下载就有法可依了。封装成一个方法如下:

function downloadFile(aLink, fileName, content){
aLink.download = fileName;
aLink.href = "data:text/plain," + content;
}

调用downloadFile之后,用户点击链接,就能触发浏览器下载。

Not enough!

但是,还不够,上面的办法有两个硬伤,会导致流失很多懒人美眉:

  1. 下载的文件类型限制死了,美眉要下载处理后的果照怎么办?
  2. 下载还要再点击一下,太麻烦啦。

要解决文件类型的问题,可以用浏览器的新API(URL.createObjectURL)来解决问题,URL.createObjectURL通常都是用来创建图片的DataURI用来显示图片,这里用来下载文件,让浏览器来帮我们设定好文件类型。

URL.createObjectURL的参数是File对象或者Blob对象,File对象也就是通过input[type=file]选择的文件,Blob对象是二进制大对象,详细说明可参考这里。

现在,我们只要用content创建一个ObjectURL并赋值给aLink即可解决文件类型的限制问题。

文件的自动下载也挺好办,自己构建一个UI点击事件,再自动触发下,就能实现自动下载啦。

现在来看看最终代码:

function downloadFile(fileName, content){
var aLink = document.createElement('a');
var blob = new Blob([content]);
var evt = document.createEvent("HTMLEvents");
evt.initEvent("click", false, false);//initEvent 不加后两个参数在FF下会报错, 感谢 Barret Lee 的反馈
aLink.download = fileName;
aLink.href = URL.createObjectURL(blob);
aLink.dispatchEvent(evt);
}

现在,只要一调用downloadFile,文件就自动下载了,是不是很爽咧,^_^。

注:目前(2014-01-02)Blob和URL.createObjectURL在标准浏览器里面都不再需要加私有前缀,可以放心使用啦啦啦~~如果你不放心,可以查查Can I Use。

本文将介绍如何使用 JavaScript 创建文件,并自动/手动将文件下载。这在导出原始数据时会比较方便。

先上代码

/**
 * 创建并下载文件
 * @param  {String} fileName 文件名
 * @param  {String} content  文件内容
 */
function createAndDownloadFile(fileName, content) {
    var aTag = document.createElement('a');
    var blob = new Blob([content]);
    aTag.download = fileName;
    aTag.href = URL.createObjectURL(blob);
    aTag.click();
    URL.revokeObjectURL(blob);
}

很简单对吧,直接调用这个方法,传入文件名和文件内容,程序新建 a 标签,新建 Blob 对象,将文件名赋给 a 标签,同时将 Blob 对象作为 Url 也赋给 a 标签,模拟点击事件,自动下载成功,最后再回收内存。下面我们来看看具体是怎么操作的。

TB16.GnOpXXXXXdapXXXXXXXXXX-307-134

Blob 对象

Blob 对象是一个字节序列。拥有 size 和 type 等属性。

拥有 2 个只读状态 OPEND 和 CLOSED。

Blob 对象属于 JavaScript Web APIs 中的 File API 规定的部分,可以参考 W3C 文档中的 The Blob Interface and Binary Data

再回来看看我们的代码里是这么写的,使用了 Blob 的构造函数:

var blob = new Blob([content]);

使用方括号的原因是,其构造函数的参数为以下4中:

  • ArrayBuffer [TypedArrays] elements.
  • ArrayBufferView [TypedArrays] elements.
  • Blob elements.
  • DOMString [WebIDL] elements.

所谓 ArrayBuffer 是一种用于呈现通用、固定长度的二进制数据的类型。详情可以参考 ArrayBuffer -MDN 以及 ECMAScript2015 标准中的 ArrayBuffer。

Blob URLs

Blob URLs 被创建或注销是使用 URL 对象上的方法。这个 URL 对象被挂在 Window (HTML) 对象下,或者 WorkerGlobalScope (Web Workers)对象下。

拥有以下静态方法 createObjectURL 和 revokeObjectURL,用于创建一个 blob 对象的 url 和注销这个 blob url。

详情可查看 关于创建和注销 Blob URL 的 W3C 标准文档

模拟 click

element.click();

在 W3C 中很早就有这个规范,不需要写繁琐的模拟事件触发的代码。

小结

目前我将这个技术使用在 天猫双十一技术和UED庆功会 的摇火箭大屏游戏中。最后的游戏结果排名,在请求了接口后,在前端直接生成并下载到了本地,作为记录保存。主要也是因为服务端暂时没有提供这个一张表去记录游戏结果,于是采用了前端记录的解决方案。

大家当时都玩的好开心啊,。你们的甘其食和全家卡的名单就是这样生成的!

参考


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流