HTML5 离线存储实战之manifest(附缓存整个文件夹的方法)

最近做了一个应用,想要在高级浏览器下离线存储图片以及样式,遂用到了html5的manifest,其中遇到不少问题,写下来总结一下。

demo:http://blog.jnecw.com/demo/id_00012/2.html

首先就是要声明 manifest的mime类型,apache下可以在httpd.conf中加上

AddType text/cache-manifest manifest
AddType text/cache-manifest .appcache

其中.appche是manifest文件的扩展名,经测试chrmoe即使不加mime类型也可以正常的解析,但是ff或者safari则必须使用.appcache的声明,否则报错。(其中试过将这两句加入到.htaccess中,失败)

然后就可以在html中调用 manifest文件了:

<!DOCTYPE html>
<html manifest="list.appcache">

manifest文件格式:

CACHE MANIFEST

# VERSION 0.3

# 直接缓存的文件
CACHE:

# 需要在线访问的文件
NETWORK:

# 替代方案
FALLBACK:

在CACHE后面写上要缓存的文件即可,在这里遇到两个小问题:

1.我缓存了首页index.html之后,页面上的图片全部都无法加载了。

HTML5 离线存储实战之manifest(附缓存整个文件夹的方法)

查了一下,原来是 首页被缓存之后页面就默认访问离线资源了,因为页面内的图片没有加入到CACHE列表中,所以一个个都显示不出来了。解决办法就是在NETWORK后面加上*号

参考下面的概念:

.零个或多个在线白名单名称空间(online whitelist namespaces)的URL
 注:在白名单区域出现的url,或被其通配符所匹配的url,都不会从离线应用程序缓存加载资源,而总是尝试从网络中获取(HTTP Cache是有效的.).
.在线白名单通配符标记(online whitelist wildcard flag),分别为open 和 blocking
 注:白名单的open状态(以通配符"*"作为首个token的白名单,则进入open状态,表示匹配所有URL,如果*不作为首个token出现,会被忽略.)表示, 所有被在线白名单名称空间 ,所匹配的url,如果没有显示的出现在CACHE项中,则都视为 该资源存在于白名单中(因为进入open状态,此时这些资源,都是遵守HTTP缓存头域相关缓存策略的.). blocking状态(即白名单内容首个token不是"*"的状态.则此时白名单中是列出具体的url,或者前缀匹配的表达式的.被匹配的部分则,同open状态匹配一样.可以根据HTTP缓存头域进行正常的访问.),则表示,manifest中没有显式出现过(也不被各类通配符所匹配的)url(自然也包括没有被白名单明确匹配的URL), 会被manifest无效处理.(无效处理,浏览器的实现就是获取不能.无法下载.)
 参考:http://www.w3.org/TR/2011/WD-html5-20110405/offline.html#changesToNetworkingModel 中的描述.会更明确的说明,无效处理,指的就是下载不能.

这样就会加载在线资源了;

2.我要缓存的images文件夹里面有好多图片,而manifest中必须一一声明文件名,这很令人头痛。于是用php写了一个方法,遍历文件夹内的所有文件,然后生成一个manifest文件,代码如下:

<?php
function list_files( $folder = '', $levels = 100 ) {
	if( empty($folder) || ! $levels){
		return false;
	}
	$files = array();
	if ( $dir = @opendir( $folder ) ) {
	while (($file = readdir( $dir ) ) !== false ) {
		if ( in_array($file, array('.', '..') ) )
			continue;
			if ( is_dir( $folder . '/' . $file ) ) {
				$files2 = list_files( $folder . '/' . $file, $levels - 1);
				if( $files2 )
				$files = array_merge($files, $files2 );
				else
				$files[] = $folder . '/' . $file . '/';
				} else {
				$files[] = $folder . '/' . $file;
			}
		}
	}
	@closedir( $dir );
	return $files;
}
function echoArray($array){
	for($i=0;$i<count($array);$i++)
	{
		echo $array[$i];
	}
}
?>
<?php
$img = list_files('images');
?>
CACHE MANIFEST

# VERSION 1.34

# 直接缓存的文件
CACHE:
<?php echoArray($img);?>

# 替代方案
FALLBACK:

将此php文件保存为list.php,chrome浏览器下可以直接这样写<html manifest=”list.php”>

但是其他支持manifest的浏览器则会报错,需要用此php生成一个扩展名为.appcache(前面声明的扩展名,也可以使其他)的文件供html调用就可以了。

另 外针对第一个问题有人用frame的方法来解决,搜了一下相关文档,应该是不可以的。他的思路是并不存储当前被访问的页面,而是用iframe引入一个页 面,在那个页面中调用manifest文件来达到存储列表内资源的目的,根据文档所写:“. a,b两个页面,引入相同资源,但a有使用manifest,而b没有.那么,即使a页面缓存了资源.b页面也不会有效.而且b页面强制更新了资源.a页 面的缓存也不会因为b的更新,而更新.”

也就是即使frame文件存储了当前页面所请求的资源,但是当前页面一样会从服务器上获取它们。此处为个人推论,未得到证实

最后附加上高人 Franky 给的资料

. 引入 manifest方式为 : <html manifest=”name.appcache”>
. manifest的加载是晚于页面其他资源的.
. manifest的contentType应为 : text/cache-manifest
. 建议其扩展名为 : appcache
. manifest文件本质是一个,要采用UTF-8编码方式编码的文本文件.
. 引入manifest的页面,即使没有被列入缓存清单中,仍然会被用户代理缓存.
. manifest文件从标准角度来说,是不能直接从缓存读取的.即使像上一条说的,你明确的把manifest放入另一个清单中.至少也是服务器尝试返回304.再去读缓存.(注1)
. 在线的情况下,用户代理每次访问页面,都会去读一次manifest.如果发现其改变, 则重新加载全部清单中的资源(注2).
. 对于浏览器来说,manifest的加载是要晚于其他资源的. 这就导致check manifest的过程是滞后的.发现manifest改变.所有浏览器的实现都是紧随这做静默更新资源.以保证下次pv,应用到更新.
. manifest文件必须与引入它的页面同源.
. 如果manifest文件是一个https或其他加密协议资源,则其清单中明示项(explicit section)的资源都必须和manifest同源.
. 备用项和备用名称空间,必须与当前的manifest同源.
. 备用项如果发生命中,则也会被缓存.
. 明示项和备用项优先级高于白名单.
. 白名单使用通配符”*”. 则会进入白名单的open状态. 这种状态下.所有不在相关Cache区域出现的url都默认使用HTTP相关缓存头策略.
. 白名单使用具体的前缀匹配或更具体的URL,则都属于blocking状态.这种状态下,白名单所匹配的,非Cache区域出现的URL,与open的*匹配的结果一致,但是不在白名单中,又不在整个manifest的资源,会block.也就是访问,加载不能.
. manifest中的url ,必须与manifest使用相同的协议.
. 一个manifest的明示项中可以包含另一个manifest.(但这种设计,我认为很2.)
. manifest中的url,不应有”#” 锚点部分出现(比如 abc.htm#1,如果出现#,则 #以及后面部分,会被丢弃.)
. 建议使用<!DOCTYPE html> DTD, 因为据说,某些浏览器会因为,进入非标准模式,而无视manifest.
(我本人没有实测,但我个人猜测,如果有这样一款浏览器,那么它很可能就是IE10. 因为IE10进入兼容模式,很多html5草案的API都使用不能.比如performance API)
. 被清单缓存的资源,是无视http cache 相关 头域, 或其是否是https资源的.
. 相同备用名称空间,不能重复出现在 备用区域中.
. 不应有相包含的备用名称空间出现在备用区域中(因为前缀匹配的原因.出现包含,显然是多余的,如果真有一个URL同时匹配两个通配符.那么就以更长的那个为准.).
. 备用名称空间 和 白名单名称空间 都使用前缀匹配模式.即支持通配符匹配模式.(可以放心的是 //www.a.com/abc 是不匹配 //www.a.com/ab的,因为//www.a.com/ab 实际上是//www.a.com/ab/)
. 前缀匹配对端口的匹配是宽松的.如abc.com:80/a.png 就会被 abc.com/所匹配.
. 在写相对路径的时候 不是相对 引入它的html 而是相对 manifest文件所在目录的
. 一但manifest检测,需要更新,导致所有cache资源更新。其中manifest会再次加载一次.(所以给所有缓存资源配置合理的304机制.是十分有必要的.)
. 一组不同的页面引入相同的manifest文件时,这组页面的即构构成一个group.并已document作为标识,来区分他们.其中任何一个的manifest或资源更新,甚至是检测都会触发其他页面的applicationCache的相应事件.
. applicationCache.update(), 只会立刻检测manifest文件,而不会更新相应资源.并且会遵守304相关http缓存头.
. a,b两个页面,引入相同资源,但a有使用manifest,而b没有.那么,即使a页面缓存了资源.b页面也不会有效.而且b页面强制更新了资源.a页面的缓存也不会因为b的更新,而更新.
. a页面引入manifest,缓存的资源, 在浏览器地址栏中直接访问,则也命中offline application的缓存.刷新也如此.至少chrome,FF都是如此实现的.
. a,b两个页面,分别引入A,B两个manifest文件,且分别缓存相同的一个资源R,则 如果此时更新R,然后更新B.则.b刷新后重新获取资源R,但是a的R资源缓存副本是不会被更新的.
. a,b两个页面,引用同一份manifest A. 则更新A,更新R,刷新b, b对应的R资源更新后,a的R资源副本也会随之更新. 这就是cache group的机制.因为a和b对应的application cache,同属于同一个application cache group.. 建议为manifest文件配置304相关 头域时,也配置expires和cache-control : max-age.因为chrome,safari,以及android,只有304相关头域,而没有expires 或 max-age时,不会有304,而只会是200, opera则无视一切http cache头域.总是200.
(浏览器的实现都有问题,webkit的问题是,没有遵守http协议.因为304相关头域是足矣使浏览器是具备资源副本,并做握手的. 而opera则完全无视http缓存头域.更加不靠谱. (IE10 pp2,FF系列.不方便测试))

转载于:https://www.cnblogs.com/shimily/articles/4270578.html