最近折騰HTML5遊戲需要離線儲存功能,便把目前可用的幾種HTML5儲存方式研究了下,基於HT for Web寫了個綜合的例項,分別利用了Cookie、WebStorage、IndexedDB以及FileSystem四種本地離線儲存方式,對燃氣監控系統的表計位置、朝向、開關以及表值等資訊做了CURD的存取操作。
HTML5的儲存還有一種Web SQL Database方式,雖然還有瀏覽器支援,是唯一的關聯式資料庫結構的儲存,但W3C以及停止對其的維護和發展,所以這裡我們也不再對其進行介紹:Beware. This specification is no longer in active maintenance and the Web Applications Working Group does not intend to maintain it further.
整個示例主要就是將HT for Web的DataModel資料模型資訊進行序列化和反序列化,這個過程很簡單通過dataModel.serialize()將模型序列化成JSON字串,通過dataModel.deserialize(jsonString)將JSON字串記憶體反序列化出模型資訊,而儲存主要就是主要就是針對JSON字串進行操作。
先介紹最簡單的儲存方式LocalStorage,程式碼如下,幾乎不用介紹就是Key-Value的簡單鍵值對儲存結構,Web Storage除了localStorage的永續性儲存外,還有針對本次回話的sessionStorage方式,一般情況下localStorage較為常用,更多可參考 http://www.w3.org/TR/webstorage/
function save(dataModel){ var value = dataModel.serialize(); window.localStorage['DataModel'] = value; window.localStorage['DataCount'] = dataModel.size(); console.log(dataModel.size() + ' datas are saved'); return value; } function restore(dataModel){ var value = window.localStorage['DataModel']; if(value){ dataModel.deserialize(value); console.log(window.localStorage['DataCount'] + ' datas are restored'); return value; } return ''; } function clear(){ if(window.localStorage['DataModel']){ console.log(window.localStorage['DataCount'] + ' datas are cleared'); delete window.localStorage['DataModel']; delete window.localStorage['DataCount']; } }
最古老的儲存方式為Cookie,本例中我只能儲存一個圖元的資訊,這種儲存方式儲存內容很有限,只適合做簡單資訊儲存,存取介面設計得極其反人類,為了介紹HTML5儲存方案的完整性我順便把他給列上:
function getCookieValue(name) { if (document.cookie.length > 0) { var start = document.cookie.indexOf(name + "="); if (start !== -1) { start = start + name.length + 1; var end = document.cookie.indexOf(";", start); if (end === -1){ end = document.cookie.length; } return unescape(document.cookie.substring(start, end)); } } return ''; } function save(dataModel) { var value = dataModel.serialize(); document.cookie = 'DataModel=' + escape(value); document.cookie = 'DataCount=' + dataModel.size(); console.log(dataModel.size() + ' datas are saved'); return value; } function restore(dataModel){ var value = getCookieValue('DataModel'); if(value){ dataModel.deserialize(value); console.log(getCookieValue('DataCount') + ' datas are restored'); return value; } return ''; } function clear() { if(getCookieValue('DataModel')){ console.log(getCookieValue('DataCount') + ' datas are cleared'); document.cookie = "DataModel=; expires=Thu, 01 Jan 1970 00:00:00 UTC"; document.cookie = "DataCount=; expires=Thu, 01 Jan 1970 00:00:00 UTC"; } }
如今比較實用強大的儲存方式為Indexed Database API,IndexedDB可以儲存結構物件,可構建key和index的索引方式查詢,目前各瀏覽器的已經逐漸支援IndexedDB的儲存方式,其使用程式碼如下,需注意IndexedDB的很多操作介面類似NodeJS的非同步回撥方式,特別是查詢時連cursor的continue都是非同步再次回撥onsuccess函式的操作方式,因此和NodeJS一樣使用上不如同步的程式碼容易。
request = indexedDB.open("DataModel"); request.onupgradeneeded = function() { db = request.result; var store = db.createObjectStore("meters", {keyPath: "id"}); store.createIndex("by_tag", "tag", {unique: true}); store.createIndex("by_name", "name"); }; request.onsuccess = function() { db = request.result; }; function save(dataModel){ var tx = db.transaction("meters", "readwrite"); var store = tx.objectStore("meters"); dataModel.each(function(data){ store.put({ id: data.getId(), tag: data.getTag(), name: data.getName(), meterValue: data.a('meter.value'), meterAngle: data.a('meter.angle'), p3: data.p3(), r3: data.r3(), s3: data.s3() }); }); tx.oncomplete = function() { console.log(dataModel.size() + ' datas are saved'); }; return dataModel.serialize(); } function restore(dataModel){ var tx = db.transaction("meters", "readonly"); var store = tx.objectStore("meters"); var req = store.openCursor(); var nodes = []; req.onsuccess = function() { var res = req.result; if(res){ var value = res.value; var node = createNode(); node.setId(value.id); node.setTag(value.tag); node.setName(value.name); node.a({ 'meter.value': value.meterValue, 'meter.angle': value.meterAngle }); node.p3(value.p3); node.r3(value.r3); node.s3(value.s3); nodes.push(node); res.continue(); }else{ if(nodes.length){ dataModel.clear(); nodes.forEach(function(node){ dataModel.add(node); }); console.log(dataModel.size() + ' datas are restored'); } } }; return ''; } function clear(){ var tx = db.transaction("meters", "readwrite"); var store = tx.objectStore("meters"); var req = store.openCursor(); var count = 0; req.onsuccess = function(event) { var res = event.target.result; if(res){ store.delete(res.value.id); res.continue(); count++; }else{ console.log(count + ' datas are cleared'); } }; }
最後是FileSystem API相當於操作本地檔案的儲存方式,目前支援瀏覽器不多,其介面標準也在發展制定變化中,例如在我寫這個程式碼時大部分文獻使用的webkitStorageInfo已被navigator.webkitPersistentStorage和navigator.webkitTemporaryStorage替代,儲存的檔案可通過filesystem:http://www.hightopo.com/persistent/meters.txt’的URL方式在chrome瀏覽器中查詢到,甚至可通過filesystem:http://www.hightopo.com/persistent/類似目錄的訪問,因此也可以動態生成圖片到本地檔案,然後通過filesystem:http:***的URL方式直接賦值給img的html元素的src訪問,因此本地儲存開啟了一扇新的門,相信以後會冒出更多稀奇古怪的奇葩應用。
navigator.webkitPersistentStorage.queryUsageAndQuota(function (usage, quota) { console.log('PERSISTENT: ' + usage + '/' + quota + ' - ' + usage / quota + '%'); } ); navigator.webkitPersistentStorage.requestQuota(2 * 1024 * 1024, function (grantedBytes) { window.webkitRequestFileSystem(window.PERSISTENT, grantedBytes, function (fs) { window.fs = fs; }); } ); function save(dataModel) { var value = dataModel.serialize(); fs.root.getFile('meters.txt', {create: true}, function (fileEntry) { console.log(fileEntry.toURL()); fileEntry.createWriter(function (fileWriter) { fileWriter.onwriteend = function () { console.log(dataModel.size() + ' datas are saved'); }; var blob = new Blob([value], {type: 'text/plain'}); fileWriter.write(blob); }); }); return value; } function restore(dataModel) { fs.root.getFile('meters.txt', {}, function (fileEntry) { fileEntry.file(function (file) { var reader = new FileReader(); reader.onloadend = function (e) { dataModel.clear(); dataModel.deserialize(reader.result); console.log(dataModel.size() + ' datas are restored'); }; reader.readAsText(file); }); }); return ''; } function clear() { fs.root.getFile('meters.txt', {create: false}, function(fileEntry) { fileEntry.remove(function() { console.log(fileEntry.toURL() + ' is removed'); }); }); }
Browser-Side的儲存方式還在快速的發展中,其實除了以上幾種外還有Application Cache,相信將來還會有新秀出現,雖然“雲”是大趨勢,但客戶端並非要走極端的“瘦”方案,這麼多年冒出了這麼多客戶端儲存方式,說明讓客戶端更強大的市場需求是強烈的,當然目前動盪階段苦逼的是客戶端程式設計師,除了要適配Mouse和Touch,還要適配各種屏,如今還得考慮適配各種儲存,希望本文能在大家選型客戶端儲存方案時有點幫助,最後上段基於HT for Web操作HTML5儲存示例的視訊效果:http://v.youku.com/v_show/id_XODUzODU2MTY0.html
<iframe src="http://player.youku.com/embed/XODUzODU2MTY0" frameborder="0" width="510" height="498"></iframe>