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之后,頁面上的圖片全部都無法加載了。

查了一下,原來是 首頁被緩存之后頁面就默認訪問離線資源了,因為頁面內的圖片沒有加入到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系列.不方便測試))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM