上篇我们介绍了cookie、Web Storage和Web数据库技术,接下来的这一篇将对文件存储和离线应用进行介绍。
文件存储
Blob
-
概念
英文全称为Binary Large Object,即二进制大对象。
-
API
// 构造函数 // blobParts参数是一个由ArrayBuffer、Blob等组成的数组,options表示一个可选对象,可指定传入的MIME类型type和行结束符写入方式endings Blob(blobParts[,options]) //文件大小,单位为B size //文件MIME类型,如"image/jpeg" type // 指定开始位置和结束位置截取指定数据,返回新的Blob对象 slice(start[[,end],contentType]) // 返回一个Promise对象,resolve结果是包含所有内容的ArrayBuffer arrayBuffer() // 返回一个Promise对象,resolve结果是包含所有内容的utf-8字符串 text() // 返回一个Promise对象,resolve结果是能读取所有内容的ReadableStream stream()
File
-
File与Blob的关系
通过执行下面表达式,可得知Blob是File的基类,所以能对Blob操作的,同样适用于File。自然,File拥有Blob的所有属性和方法。
file instanceof Blob; //true
-
常见获取方式
1.从input类型为file,返回的FileList中获取
2.从放置被拖拽对象到指定位置,返回的DataTransfer获取
3.构造函数创建
-
属性
// 文件最近一次的修改时间时间戳 lastModified // 文件最近一次的修改时间Date对象 lastModifiedDate // 文件名,含后缀 name // 文件相对于用户选中的目录的相对路径,非标准,不推荐使用 webkitRelativePath
URL
-
概念
用于解析、构造、规范和编码URL。便于读取或修改URL内容。
-
API
//为便于理解各属性对应的内容,以var url = new URL("http://user:mudong@mudong.xyz/main?data=123#abc");作为示例。 //构造函数 // url参数可以绝对url或相对url,为相对url时需指定base参数,作参照路径。 URL(url[,base]) //哈希值,如上为#abc hash //主机名,如上为mudong.xyz host //完整链接,如上为http://user:mudong@mudong.xyz/main?data=123#abc href //源,如上为http://mudong.xyz origin //密码,如上为mudong password //路径名,如上为/main pathname //端口号,如上为"" port //协议名,如上为”http“ protocol //参数字符串,如上为"?data=123" search //参数字符串对象,提供参数字符串的基本操作方法: // append // delete // entries // forEach // get // getAll // has // keys // set // sort // toString // values //如上通过url.searchParams.get('data')为123 searchParams //用户名,如上为name username // 返回整个url toString() // 返回整个url toJson() // 创建一个DOMString,包含一个唯一的blob链接。参数可以是File、Blob或MediaSource // 示例: // URL.createObjectURL(new Blob([new ArrayBuffer(16)])) // "blob:chrome://new-tab-page/3ee0ed55-a791-4e07-80e9-186d9029fe64" URL.createObjectURL(obj) // 销毁之前使用URL.createObjectURL创建的URL实例。参数是blob链接字符串 // 示例: // URL.revokeObjectURL("blob:chrome://new-tab-page/3ee0ed55-a791-4e07-80e9-186d9029fe64") // undefined URL.revokeObjectURL(url)
-
blob://URL、data://URL、file://URL的区别
data://URL对内容进行编码。
blob://URL对浏览器存储在内存中或者磁盘上的Blob的一个简单引用。
file:URL指向本地系统文件的一个文件,仅暴露文件路径、浏览目录的许可等,除此之外任何内容都会带来安全问题。
-
特点
blob:URL与创建其的脚本拥有相同源,只在同源的文档中有效,而file:URL是非同源的,相较起来blob:URL更灵活些。
blob:URL不是永久有效,一旦用户关闭或者离开包含创建Blob URL脚本的文档,该BLob URL就失效了。
blob:URL只允许通过GET请求获取,并且一旦获取成功,浏览器必须返回一个HTTP 200 OK的状态码,同时返回一个使用Blob type属性的Content-Type头部信息。
FileReader
-
概念
使Web应用能异步读取存储于计算机本地的文件内容或二进制缓冲数据。
-
API
//读文件产生的错误异常 error //读状态,值可能为0,1,2,分别表示数据未加载,数据正加载,完成读操作 readyState //文件内容 result //停止读操作 abort() //开始读数据内容,并以ArrayBuffer返回 readAsArrayBuffer() //开始读数据内容,并以二进制字符串返回 readAsBinaryString() //开始读数据内容,并以dataURL返回 readAsDataURL() //开始读数据内容,并以普通文本返回 readAsText() //监听停止读事件 onabort //监听读错误事件 onerror //监听读成功完成事件 onload //监听读开始事件 onloadstart //监听读完成事件,不论成功与否 onloadend //监听读进度事件 onprogress
其他文件API
-
FileSystem/FileSystemSync
表示一个文件系统。通过window.requestFileSystem()访问。有两个属性,一个表示文件系统名的name属性,另一个表示文件系统根目录的root属性
-
FileSystemEntry/FileSystemEntrySync
表示文件系统中的一个单实体,可能是文件,也可能是目录。在拖拽使用中,通过DataTransferItem.webkitGetAsEntry()访问。具体api如下,
//获取该实体附属的文件系统 filesystem //获取从根路径到该实体的完整绝对路径 fullPath //实体是否是目录 isDirectory //实体是否是文件 isFile //实体名称 name // 复制实体到指定位置 copyTo() // 获取关于文件的元数据 getMetadata() // 获取父目录实体 getParent() // 移动实体到指定位置,或重命名实体 moveTo() // 删除指定文件或目录,注意删除的目录必须是空的 remove() // 返回识别该实体的url,形如filesystem:URL toURL()
-
FileSystemFileEntry/FileSystemFileEntrySync
表示文件系统中的一个文件。api同FileSystemEntry,只有一个独有方法file,创建一个新的可读文件对象并返回
-
FileSystemDirectoryEntry/FileSystemDirectoryEntrySync
表示文件系统中的一个目录。api同FileSystemEntry,独有方法如下,
// 创建FileSystemDirectoryReader对象,用于读取目录的中实体 createReader() //获取指定的目录 getDirectory() //获取目录下的指定文件 getFile()
-
FileSystemDirectoryReader/FileSystemDirectoryReaderSync
用于访问目录下所有实体。只有一个方法readEntries,返回该目录下的所有实体数组。
Base64
-
概念
一种编码格式,即1个字节中只用6位存储数据,也就是说,原本用3个字节存储的数据,转换为base64则需占用4个字节进行存储。
希望能在处理文本数据的媒介上存储和传输二进制数据而设计。
-
提供的相关方法
编码为base64:
btoa(str)
解码base64:
atob(base64Str)
-
unicode问题
由于DOMString是16位编码的字符串,所以如果有字符超出了8位ASCII编码的字符范围时,在大多浏览器中对Unicode字符串调用btoa将会造成一个Character Out Of Range的异常。
需对方法增强,使之支持UTF16。以下是摘自MDN的其中一种实现方式。
function btoaUTF16(str) { var u16Arr = new Uint16Array(str.length); Array.prototype.forEach.call(u16Arr, function(el, idx, arr) { arr[idx] = str.charCodeAt(idx); }); return btoa(String.fromCharCode.apply(null, new Uint8Array(u16Arr.buffer))); } function atobUTF16(base64) { var binary = atob(base64); var u8Arr = new Uint8Array(binary.length); Array.prototype.forEach.call(u8Arr, function(el, idx, arr) { arr[idx] = binary.charCodeAt(idx); }); return String.fromCharCode.apply(null, new Uint16Array(u8Arr.buffer)); }
Blob、Base64、ArrayBuffer间的转换
-
原理
利用FileReader API实现从Blob到base64字符串、ArrayBuffer的转换。
利用Typed Array实现base64字符串、ArrayBuffer到Blob的转换。
-
代码实现
//Blob转base64
function blobToB64(blob, cb) {
var reader = new FileReader();
reader.addEventListener('load', function() {
cb && cb(reader.result);
});
reader.readAsDataURL(blob, {
type: blob.type
});
}
//Blob转ArrayBuffer
function blobToAb(blob, cb) {
var reader = new FileReader();
reader.addEventListener('load', function() {
cb && cb(reader.result);
});
reader.readAsArrayBuffer(blob);
}
//ArrayBuffer转base64
function abToB64(arraybuffer, cb) {
blobToB64(abToBlob(arraybuffer), cb);
}
//ArrayBuffer转Blob
function abToBlob(arraybuffer, filename) {
var u8arr = new Uint8Array(arraybuffer);
if (filename) {
return new File([u8arr], filename);
} else {
return new Blob([u8arr])
}
}
//base64转ArrayBuffer
function b64ToAb(base64, cb) {
blobToAb(b64ToBlob(base64), cb);
}
//base64格式转Blob
function b64ToBlob(base64, filename) {
var arr = base64.split(',');
var mime = arr[0].match(/:(.*?);/)[1];
var binaryStr = atob(arr[1]);
var i = binaryStr.length;
var u8arr = new Uint8Array(i);
while (i--) {
u8arr[i] = binaryStr.charCodeAt(i);
}
if (filename) {
return new File([u8arr], filename, {
type: mime
});
} else {
return new Blob([u8arr], {
type: mime
})
}
}
离线Web应用
理解离线
无网络下仍然可以访问不随着用户清除浏览器缓存而被清除旧数据会被最近一次访问的新数据覆盖
离线配置
-
配置应用程序缓存清单文件清单文件后缀名是 appcache。服务端响应清单文件时,需设置响应头的内容类型为'text/cache-manifest',否则不能缓存应用程序了。清单文件格式要求第一行必须为 CACHE MANIFEST,接下来紧跟要缓存的资源列表,一行一个资源。注释以#开头。示例如下,CACHE MANIFEST # 应用要缓存的资源列表 CACHE: index.html index.css index.js images/logo.png # NETWORK区域标识该URL资源总通过网络获取。URL支持*通配符,表示任何不在清单中的资源,浏览器都将通过网络加载 NETWORK: list.html # FALLBACK区域的清单项每行包括两个URL,第一个是URL前缀,第二个是需要加载存储在缓存中的资源。匹配该前缀的URL所指定的资源从网络载入失败,就会用从缓存中访问第二个URL指定的资源代替 FALLBACK: /articles 404.html
-
在需要缓存的 HTML 页面指定缓存清单文件,在单页应用中有且只有一个页面需 被缓存,而在多页应用中可能会有多个<!DOCTYPE html> <html manifest="app.appcache"> ... </html>
提问:如何“卸载”离线 Web 应用? 1.服务端删除清单文件 2.删除缓存的 HTML 页面中对清单文件的配置
缓存更新
缓存更新机制
在线状态下,浏览器异步检查清单文件是否有更新,若有,则新的清单文件及清单中列举的所有资源文件都将被下载,重新保存到应用程序缓存中。
提问:资源文件变化了,清单文件没变化,而浏览器只会检查清单文件是否变化。要让资源文件能重新下载,该怎么解决?每次资源文件发生变化,可以修改清单文件的版本号,这样保证能检测到清单文件变化,重新下载清单中的所有资源文件。
提问:为什么更新了资源后,访问时却不是更新后的资源?浏览检查清单文件和下载资源是异步的,也就是说,应用可能在资源下载未完成前先从缓存加载资源了,所以通常都是第二次访问才能看到更新的资源。
-
缓存对象 applicationCache事件:onchecking每载入设置 manifest 属性的 HTML 文件时触发onnoupdate应用程序清单文件无变动时触发ondownloading应用程序清单文件有变动,监听清单文件和要缓存的资源列表的下载过程onprogress监听应用程序的清单文件和要缓存的资源列表的下载进度oncached未曾缓存的应用程序下载清单文件和资源结束后触发onupdateready曾缓存的应用程序下载清单文件和资源结束后触发onerror处于离线状态,无法检查清单文件时触发onobsolete处于在线状态,应用程序有缓存,但清单文件不存在时触发,且将应用程序从缓存中删除属性:UNCACHED应用程序无设置 manifest 属性,未缓存IDLE清单文件检查完毕,并缓存了最新的应用程序CHECKING浏览器正在检查清单文件DOWNLOADING浏览器正在下载并缓存清单中列举的所有文件UPDATEREADY已经下载和缓存了最新版的应用程序OBSOLETE清单文件不存在,缓存将被清除方法:update显式调用更新缓存算法,检测是否有最新版本的应用程序。swapCache通知浏览器弃用旧缓存,所有请求都从新缓存中获取。当状态属性为 UPDATEREADY 或 OBSOLETE 时,调用该方法才有意义,其他状态属性下调用直接抛异常。
其他
浏览器在线状态的检测与监听
-
检测浏览器是否在线navigator.online 属性 -
监听浏览器在线状态变化window.onoffline 事件window.ononline 事件
离线应用数据同步
离线状态下,通过 localStorage 保存应用相关数据,在线状态下,将本地数据同步到服务器。
参考
-
JavaScript权威指南(第6版)
-
ES6标准入门(第2版)
到了这里,关于存储与离线的学习算告一段落了。感谢阅读!!!