工作中有时需要通过 JavaScript 保存文件到本地,我们都知道 JavaScript 基于安全的考虑,是不允许直接操作本地文件的。
IE 可以通过 VB 插件的方式进行,而 Chrome 和 firefox 都不支持 JavaScript 向本地写入文件,所以 VB 插件的方式存在兼容性问题。
那有没有适合的方法呢?答案是肯定的,我们可以通过 FileSaver.js 这个小插件实现我们的需求。下面看一段具体的代码吧:
// 此处案例用来抓取阿里地图数据
function demo() {
var id = '100000'
var num = 0
var time = 0 // 如果使用for循环,文件过多,可能会丢失文件,故使用延迟
function bound(id) {
setTimeout(() => {
$.ajax({
url: 'https://datavmap-public.oss-cn-hangzhou.aliyuncs.com/areas/bound/' + id + '.json',
success(res) {
// num++
// console.log(num)
// downloadTextFile(id, JSON.stringify(res))
}
})
}, time)
time += 200
}
function children(id) {
$.ajax({
url: 'https://datavmap-public.oss-cn-hangzhou.aliyuncs.com/areas/children/' + id + '.json',
success(res) {
// num++
// console.log(num)
bound(id)
// downloadTextFile(id, JSON.stringify(res))
if (res.features.length) {
for (let i = 0; i < res.features.length; i++) {
const adcode = res.features[i].properties.adcode
if (!res.features[i].properties.childrenNum) { // 如果没有子集数据
bound(adcode)
continue
}
if (adcode != id) {
children(adcode)
}
}
}
}
})
}
children(id)
}
demo()
/**
* 下载文件
* name 文件名
* mobileCode 文件内容
*/
var downloadTextFile = function (name, mobileCode) {
if (!mobileCode) {
mobileCode = ''
}
// 采用的字符编码格式为“UTF-8”,这样就避免的中文乱码的问题。
var file = new File([mobileCode], name + ".json", { type: "text/plain;charset=utf-8" })
saveAs(file)
}
附 FileSaver.js 文件的完整源码:
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.3.2
* 2016-06-16 18:25:19
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/* global self */
/* jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs || (function (view) {
'use strict'
// IE <10 is explicitly unsupported
if (typeof view === 'undefined' || typeof navigator !== 'undefined' && /MSIE [1-9]\./.test(navigator.userAgent)) {
return
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
var get_URL = function () {
return view.URL || view.webkitURL || view
}
var save_link = doc.createElementNS('http://www.w3.org/1999/xhtml', 'a')
var can_use_save_link = 'download' in save_link
var click = function (node) {
var event = new MouseEvent('click')
node.dispatchEvent(event)
}
var is_safari = /constructor/i.test(view.HTMLElement) || view.safari
var is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent)
var throw_outside = function (ex) {
(view.setImmediate || view.setTimeout)(function () {
throw ex
}, 0)
}
var force_saveable_type = 'application/octet-stream'
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
var arbitrary_revoke_timeout = 1000 * 40 // in ms
var revoke = function (file) {
var revoker = function () {
if (typeof file === 'string') { // file is an object URL
get_URL().revokeObjectURL(file)
} else { // file is a File
file.remove()
}
}
setTimeout(revoker, arbitrary_revoke_timeout)
}
var dispatch = function (filesaver, event_types, event) {
event_types = [].concat(event_types)
var i = event_types.length
while (i--) {
var listener = filesaver['on' + event_types[i]]
if (typeof listener === 'function') {
try {
listener.call(filesaver, event || filesaver)
} catch (ex) {
throw_outside(ex)
}
}
}
}
var auto_bom = function (blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
}
return blob
}
var FileSaver = function (blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob)
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
var type = blob.type
var force = type === force_saveable_type
var object_url
var dispatch_all = function () {
dispatch(filesaver, 'writestart progress write writeend'.split(' '))
}
// on any filesys errors revert to saving with object URLs
var fs_error = function () {
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader()
reader.onloadend = function () {
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;')
var popup = view.open(url, '_blank')
if (!popup) view.location.href = url
url = undefined // release reference before dispatching
filesaver.readyState = filesaver.DONE
dispatch_all()
}
reader.readAsDataURL(blob)
filesaver.readyState = filesaver.INIT
return
}
// don't create more object URLs than needed
if (!object_url) {
object_url = get_URL().createObjectURL(blob)
}
if (force) {
view.location.href = object_url
} else {
var opened = view.open(object_url, '_blank')
if (!opened) {
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
view.location.href = object_url
}
}
filesaver.readyState = filesaver.DONE
dispatch_all()
revoke(object_url)
}
filesaver.readyState = filesaver.INIT
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob)
setTimeout(function () {
save_link.href = object_url
save_link.download = name
click(save_link)
dispatch_all()
revoke(object_url)
filesaver.readyState = filesaver.DONE
})
return
}
fs_error()
}
var FS_proto = FileSaver.prototype
var saveAs = function (blob, name, no_auto_bom) {
return new FileSaver(blob, name || blob.name || 'download', no_auto_bom)
}
// IE 10+ (native saveAs)
if (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob) {
return function (blob, name, no_auto_bom) {
name = name || blob.name || 'download'
if (!no_auto_bom) {
blob = auto_bom(blob)
}
return navigator.msSaveOrOpenBlob(blob, name)
}
}
FS_proto.abort = function () { }
FS_proto.readyState = FS_proto.INIT = 0
FS_proto.WRITING = 1
FS_proto.DONE = 2
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null
return saveAs
}(
typeof self !== 'undefined' && self ||
typeof window !== 'undefined' && window ||
this.content
))
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== 'undefined' && module.exports) {
module.exports.saveAs = saveAs
} else if ((typeof define !== 'undefined' && define !== null) && (define.amd !== null)) {
define('FileSaver.js', function () {
return saveAs
})
}