您的位置:首页 > 其它

兼容多浏览器的本地存储

2014-03-19 17:27 218 查看
在做考试系统时需要解决这样一个问题,就是当考生考试时可能出出现断网、关闭浏览器,刷新等问题,数据此时可能没有及时写入数据库,所以造成数据丢失问题,,所以这里就需要用到本地存储,以前的版本都是用cookie,但是不得不说cookie太小了,只有可怜的4k,而机房的网有时候实在是让人捉急,所以,考虑换一种方案。

直接读取XML实现方式

因为本地存储平时接触的不是太多,开始想的只是简单的读取本地的XML,做起来倒是挺容易的,一个语句就能搞定:

<script language="javascript"type="text/javascript">

var fso, f1, ts, s;

varForReading = 1;

fso= new ActiveXObject("Scripting.FileSystemObject");

//创建文件

f1 =fso.CreateTextFile("F:\\testfile.txt", true);

//填写一行数据

f1.WriteLine("我是美丽的姑娘");

f1.WriteBlankLines(1);

//关闭文件

f1.Close();

//打开文件

ts= fso.OpenTextFile("F:\\list.xml", ForReading);

var nodes = ts.documentElement.childNodes;

//读取文件一行内容到字符串

s= ts.ReadLine();

//显示字符串信息

alert("文件信息是: "+s);

//关闭文件

ts.Close();

</script>

但是这样写虽然简单,带来的弊端也不少,于是上网查到UserData可以解决这个问题。

UserData实现方式:

userData行为通过将数据写入一个UserData存储区(UserDatastore)来保存数据,userData可以将数据以XML格式保存在客户端计算机上,如果你用的是 Windows 2000 或者 Windows XP,是保存在C:\Documents and Settings\Liming\UserData\文件夹下(如果操作系统不是安装在C盘,那么C就应该是操作系统所在的分区)。

该数据将一直存在,除非你人为删除或者用脚本设置了该数据的失效期。

userData行为提供了一个比Cookie更具有动态性和更大容量的数据结构。每页的UserData存储区数据大小可以达到64 Kb,每个域名可以达到640 Kb。

userData行为通过sessions为每个对象分配UserData存储区。使用save和load方法将UserData存储区数据保存在缓存(cache)中。一旦UserData存储区保存以后,即使IE浏览器关闭或者刷新了,下一次进入该页面,数据也能够重新载入而不会丢失。

但是,UserData是IE的东西,所以其他浏览器不兼容,当时,因为现在做的这个考试系统一般情况下都在UserData下使用,所以也实现了一下:

[html] view plaincopy





<HTML>

<HEAD>

<STYLE>

.userData{behavior:url(#default#userdata);}

</STYLE>

<SCRIPT>

function fnSaveInput(){

var oPersist=oPersistForm.oPersistInput;

oPersist.setAttribute("sPersist",oPersist.value);//将oPersist.value存储为sPersist属性

oPersist.save("oXMLBranch"); //存储在名为oXMLBranch的UserData存储区

}

function fnLoadInput(){

var oPersist=oPersistForm.oPersistInput;

varabc=oPersist.load("oXMLBranch"); //载入在名为oXMLBranch的UserData存储区

oPersist.value=oPersist.getAttribute("sPersist");//将sPersist属性赋值给oPersist.value

}

</SCRIPT>

</HEAD>

<BODY>

<FORM ID="oPersistForm">

<INPUT CLASS="userData"TYPE="text" ID="oPersistInput">

<INPUT TYPE="button"VALUE="Load" onclick="fnLoadInput()">

<INPUT TYPE="button"VALUE="Save" onclick="fnSaveInput()">

</FORM>

</BODY>

</HTML>

HTML5 LocalStorage

但是,毕竟UserData有他的局限性,处于兼容考虑,也在实现过程中听说了一种叫HTML5 LocalStorage 的东西。

要介绍HTML5 LocalStorage,首先介绍一下本地存储的历史:



从这里可以看出去,本地存储一路从Cookies走到最后,真心的不容易,那么,HTML5 LocalStorage 具体有什么好处呢?

最早的Cookies自然是大家都知道,问题主要就是太小,大概也就4KB的样子,而且IE6只支持每个域名20个cookies,太少了。优势就是大家都支持,而且支持得还蛮好。很早以前那些禁用cookies的用户也都慢慢的不存在了,就好像以前禁用javascript的用户不存在了一样。

userData是IE的东西。现在用的最多的是Flash,空间是Cookie的25倍,基本够用。再之后Google推出了Gears,虽然没有限制,但不爽的地方就是要装额外的插件。到了HTML5把这些都统一了,官方建议是每个网站5MB,非常大了,就存些字符串,足够了。比较诡异的是居然所有支持的浏览器目前都采用的5MB,尽管有一些浏览器可以让用户设置,但对于网页制作者来说,目前的形势就5MB来考虑是比较妥当的。

那么,有哪些浏览器支持他呢?



所以根据图我们可以看出,技术所以的浏览器都支持HTML5 LocalStorage,所以,这也让本地存储前途一片光明啊!

下面写一个简单的实例:

<body>

<p>You have viewed this page <spanidspanid="count">0</span> time(s).</p>

<p><input type="button"value="changeStorage" onClick="changeS()"/></p>

<script>

varstorage = window.localStorage;

if(!storage.getItem("pageLoadCount"))storage.setItem("pageLoadCount",0);

storage.pageLoadCount= parseInt(storage.getItem("pageLoadCount")) + 1;//必须格式转换

document.getElementById("count").innerHTML= storage.pageLoadCount;

showStorage();

if(window.addEventListener){

window.addEventListener("storage",handle_storage,false);

}elseif(window.attachEvent){

window.attachEvent("onstorage",handle_storage);

}

functionhandle_storage(e){

if(!e){e=window.event;}

showObject(e);

}

functionshowObject(obj){

//递归显示object

if(!obj){return;}

for(var i in obj){

if(typeof(obj[i])!="object" ||obj[i]==null){

document.write(i+ " : " + obj[i] + "<br/>");

} else{

document.write(i+ " : object" + "<br/>");

}

}

}

storage.setItem("a",5);

functionchangeS(){

//修改一个键值,测试storage事件

if(!storage.getItem("b")){storage.setItem("b",0);}

storage.setItem('b',parseInt(storage.getItem('b'))+1);

}

functionshowStorage(){

//循环显示localStorage里的键值对

for(var i=0;i<storage.length;i++){

//key(i)获得相应的键,再用getItem()方法获得对应的值

document.write(storage.key(i)+ " :" + storage.getItem(storage.key(i)) + "<br>");

}

}

</script>

</body>

当运行这个实例的时候,出现一个诡异的问题,就是其他浏览器还好,在IE下报错,所以,这也算是他的一个特点吧,它在IE、Firefox测试的时候需要把文件上传到服务器上(或者localhost),直接点开本地的HTML文件,是不行的,这里一定要注意。

最后,这些demo都有了,我们详细的讲一下它的一些属性:



到这里,基本上基本的概念和简单的demo就都清楚了,现在需要的就是把这些东西整合到一起,因为我们知道HTML5 LocalStorage不能支持IE8以下的浏览器,但是考试系统运行的环境大部分都在IE8以下,所以我们如果想做一个兼容性比较好的本地存储,就需要UserData和HTML5 LocalStorage连起来用,皇天不负有心人,在实现的过程中,我在网上找到了这样好的代码,毕竟以实现程序为主,又好使的,还是用用好使的吧。下面是代码:

我是借鉴的代码哦

但是这个文章里没有对命名空间给出代码,所以直接使用是有问题的,需要加入命名空间的代码,以下是我稍微改动了一下,并且适应于考试系统的代码:

完整的demo

HTML代码(注意,一定要放在服务器上运行,IIS,TOMCAL,JBOSS都可以,不要直接点击运行):

<!DOCTYPE html PUBLIC "-//W3C//DTDXHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<htmlxmlnshtmlxmlns="http://www.w3.org/1999/xhtml">

<head>

<metahttp-equivmetahttp-equiv="Content-Type" content="text/html;charset=gb2312" />

<title>无标题文档</title>

<script type="text/javascript"src="js/jquery-1.4.2.min.js"></script>

<script type="text/javascript" src="js/jquery.utils.js"></script>

<script type="text/javascript"src="js/localstorage.js"></script>

<script>

function saveStu(){

qext.LocalStorage.save("2344234","234","234234","234234");

}

function saveMore(){

// 保存多个对象

qext.LocalStorage.set([{

key : "username",

value : "{ ‘sitename’: ‘K JSON’, ‘siteurl’: ‘www.kjson.com’, ‘keyword’: ‘JSON在线校验,格式化JSON,json 在线校验’}",

expires : 3600 * 1000 /*单位:ms*/

},{

key : "password",

value : "zxlie",

expires : 3600 * 1000 /*单位:ms*/

}]);

}

function show(){

//获取某一个本地存储,返回值为:{key:"",value:"",expires:""},未取到值时返回值为:null

varrst = qext.LocalStorage.get({

key : "2344234234234234"

});

alert(rst);

}

function getMore(){

vararrGet = qext.LocalStorage.getAllKeys();

for(vari=0;i<arrGet.length;i++){

varrst = qext.LocalStorage.get({

key : arrGet[i]

});

alert(rst);

}

}

function removeOne(){

qext.LocalStorage.remove({

key : "2344234234234"

})

}

</script>

</head>

<body>

<input type="button"onclick="saveStu()" value="保存数据" />

<input type="button"onclick="show()" value="显示数据" />

<input type="button"onclick="getMore()" value="显示全部数据" />

<input type="button"onclick="removeOne()" value="移除一个数据" />

</body>

</html>

JS代码:

/**

* 注册命名空间

*@param {String} fullNS 完整的命名空间字符串,如qui.dialog

*@param {Boolean} isIgnorSelf 是否忽略自己,默认为false,不忽略

*@author

*@example

* window.registNS("QingFeed.Text.Bold");

*/

window.registNS =function(fullNS,isIgnorSelf){

//命名空间合法性校验依据

var reg = /^[_$a-z]+[_$a-z0-9]*/i;

// 将命名空间切成N部分, 比如baidu.libs.Firefox等

var nsArray = fullNS.split('.');

var sEval = "";

var sNS = "";

var n = isIgnorSelf ? nsArray.length - 1 : nsArray.length;

for (var i = 0; i < n; i++){

//命名空间合法性校验

if(!reg.test(nsArray[i])) {

throw new Error("Invalid namespace:" + nsArray[i] +"");

return ;

}

if (i != 0) sNS += ".";

sNS += nsArray[i];

// 依次创建构造命名空间对象(假如不存在的话)的语句

sEval += "if(typeof(" + sNS + ")=='undefined') " +sNS + "=new Object();else " + sNS + ";";

}

//生成命名空间

if (sEval != "") {

return eval(sEval);

}

return {};

};

/**

* 注册命名空间

*/

window.registNS('qext');

/**

*@class qext.LocalStorage

* 跨浏览器的本地存储实现。高级浏览器使用localstorage,ie使用UserData。虽然说是本地存储,也请不要存储过大数据,最好不要大于64K.

* 因为ie下UserData每页最大存储是64k。

*@singleton

*@author zhaoxianlie (xianliezhao@foxmail.com)

*/

(function(){

/**

* 验证字符串是否合法的键名

* @param {Object} key 待验证的key

* @return {Boolean} true:合法,false:不合法

* @private

*/

function _isValidKey(key) {

return (newRegExp("^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+\x24")).test(key);

}

//所有的key

var _clearAllKey = "_baidu.ALL.KEY_";

/**

* 创建并获取这个input:hidden实例

* @return {HTMLInputElement} input:hidden实例

* @private

*/

function _getInstance(){

//把UserData绑定到input:hidden上

var _input = null;

//是的,不要惊讶,这里每次都会创建一个input:hidden并增加到DOM树种

//目的是避免数据被重复写入,提早造成“磁盘空间写满”的Exception

_input = document.createElement("input");

_input.type = "hidden";

_input.addBehavior("#default#userData");

document.body.appendChild(_input);

return _input;

}

/**

* 将数据通过UserData的方式保存到本地,文件名为:文件名为:config.key[1].xml

* @param {String} key 待存储数据的key,和config参数中的key是一样的

* @param {Object} config 待存储数据相关配置

* @cofnig {String} key 待存储数据的key

* @config {String} value 待存储数据的内容

* @config {String|Object} [expires] 数据的过期时间,可以是数字,单位是毫秒;也可以是日期对象,表示过期时间

* @private

*/

function __setItem(key,config){

try {

var input = _getInstance();

//创建一个Storage对象

var storageInfo = config || {};

//设置过期时间

if(storageInfo.expires) {

var expires;

//如果设置项里的expires为数字,则表示数据的能存活的毫秒数

if ('number' == typeofstorageInfo.expires) {

expires = new Date();

expires.setTime(expires.getTime() + storageInfo.expires);

}

input.expires =expires.toUTCString();

}

//存储数据

input.setAttribute(storageInfo.key,storageInfo.value);

//存储到本地文件,文件名为:storageInfo.key[1].xml

input.save(storageInfo.key);

} catch (e) {

}

}

/**

* 将数据通过UserData的方式保存到本地,文件名为:文件名为:config.key[1].xml

* @param {String} key 待存储数据的key,和config参数中的key是一样的

* @param {Object} config 待存储数据相关配置

* @cofnig {String} key 待存储数据的key

* @config {String} value 待存储数据的内容

* @config {String|Object} [expires] 数据的过期时间,可以是数字,单位是毫秒;也可以是日期对象,表示过期时间

* @private

*/

function _setItem(key,config){

//保存有效内容

__setItem(key,config);

//下面的代码用来记录当前保存的key,便于以后clearAll

var result = _getItem({key : _clearAllKey});

if(result) {

result = {

key : _clearAllKey,

value : result

};

} else {

result = {

key : _clearAllKey,

value : ""

};

}

if(!(new RegExp("(^|\\|)" + key + "(\\||$)",'g')).test(result.value)){

result.value += "|" + key;

//保存键

__setItem(_clearAllKey,result);

}

}

/**

* 提取本地存储的数据

* @param {String} config 待获取的存储数据相关配置

* @cofnig {String} key 待获取的数据的key

* @return {String} 本地存储的数据,获取不到时返回null

* @example

* qext.LocalStorage.get({

* key : "username"

* });

* @private

*/

function _getItem(config){

try {

var input = _getInstance();

//载入本地文件,文件名为:config.key[1].xml

input.load(config.key);

//取得数据

return input.getAttribute(config.key) || null;

} catch (e) {

return null;

}

}

/**

* 移除某项存储数据

* @param {Object} config 配置参数

* @cofnig {String} key 待存储数据的key

* @private

*/

function _removeItem(config){

try{

varinput = _getInstance();

//载入存储区块

input.load(config.key);

//移除配置项

input.removeAttribute(config.key);

//强制使其过期

varexpires = new Date();

expires.setTime(expires.getTime()- 1);

input.expires= expires.toUTCString();

input.save(config.key);

//从allkey中删除当前key

//下面的代码用来记录当前保存的key,便于以后clearAll

var result = _getItem({key :_clearAllKey});

if(result){

result= result.replace(new RegExp("(^|\\|)" + config.key +"(\\||$)",'g'),'');

result= {

key: _clearAllKey,

value: result

};

//保存键

__setItem(_clearAllKey,result);

}

}catch (e) {

}

}

//移除所有的本地数据

function _clearAll(){

result = _getItem({key : _clearAllKey});

if(result) {

var allKeys = result.split("|");

var count = allKeys.length;

for(var i = 0;i < count;i++){

if(!!allKeys[i]) {

_removeItem({key:allKeys[i]});

}

}

}

}

/**

* 获取所有的本地存储数据对应的key

* @return {Array} 所有的key

* @private

*/

function _getAllKeys(){

var result = [];

var keys = _getItem({key : _clearAllKey});

if(keys) {

keys = keys.split('|');

for(var i = 0,len = keys.length;i < len;i++){

if(!!keys[i]) {

result.push(keys[i]);

}

}

}

return result ;

}

/**

* 判断当前浏览器是否支持本地存储:window.localStorage

* @return {Boolean} true:支持;false:不支持

* @remark 支持本地存储的浏览器:IE8+、Firefox3.0+、Opera10.5+、Chrome4.0+、Safari4.0+、iPhone2.0+、Andrioid2.0+

* @private

*/

var _isSupportLocalStorage = (('localStorage' in window) &&(window['localStorage'] !== null)),

_isSupportUserData = !!jQuery.browser.ie;

qext.LocalStorage = {

/**

* 如果支持本地存储,返回true;否则返回false

* @type Boolean

*/

isAvailable : _isSupportLocalStorage || _isSupportUserData,

/**

* 将数据进行本地存储(只能存储字符串信息)

* <pre><code>

* //保存单个对象

* qext.LocalStorage.set({

* key: "username",

* value: "baiduie",

* expires: 3600 * 1000

* });

* //保存对个对象

* qext.LocalStorage.set([{

* key: "username",

* value: "baiduie",

* expires: 3600 * 1000

* },{

* key: "password",

* value: "zxlie",

* expires: 3600 * 1000

* }]);

* </code></pre>

* @param {Object} obj 待存储数据相关配置,可以是单个JSON对象,也可以是由多个JSON对象组成的数组

* <ul>

* <li><b>key</b> : String <divclassdivclass="sub-desc">待存储数据的key,务必将key值起的复杂一些,如:baidu.username</div></li>

* <li><b>value</b> : String <divclassdivclass="sub-desc">待存储数据的内容</div></li>

* <li><b>expires</b> : String/Object (Optional)<divclassdivclass="sub-desc">数据的过期时间,可以是数字,单位是毫秒;也可以是日期对象,表示过期时间</div></li>

* </ul>

*/

set : function(obj){

//保存单个对象

var_set_ = function(config){

//key校验

if(!_isValidKey(config.key)){return;}

//待存储的数据

varstorageInfo = config || {};

//支持本地存储的浏览器:IE8+、Firefox3.0+、Opera10.5+、Chrome4.0+、Safari4.0+、iPhone2.0+、Andrioid2.0+

if(_isSupportLocalStorage){

window.localStorage.setItem(storageInfo.key,storageInfo.value);

if(config.expires){

var expires;

//如果设置项里的expires为数字,则表示数据的能存活的毫秒数

if ('number' == typeofstorageInfo.expires) {

expires = newDate();

expires.setTime(expires.getTime() + storageInfo.expires);

}

window.localStorage.setItem(storageInfo.key +".expires",expires);

}

}else if(_isSupportUserData) { //IE7及以下版本,采用UserData方式

_setItem(config.key,storageInfo);

}

};

//判断传入的参数是否为数组

if(obj&& obj.constructor === Array && obj instanceof Array){

for(vari = 0,len = obj.length;i < len;i++){

_set_(obj[i]);

}

}elseif(obj){

_set_(obj);

}

},

/**

* 提取本地存储的数据

* <pre><code>

* //获取某一个本地存储,返回值为:{key:"",value:"",expires:""},未取到值时返回值为:null

* var rst = qext.LocalStorage.get({

* key: "username"

* });

* //获取多个本地存储,返回值为:["","",""],未取到值时返回值为:[null,null,null]

* qext.LocalStorage.get([{

* key: "username"

* },{

* key: "password"

* },{

* key: "sex"

* }]);

* </code></pre>

* @param {String} obj 待获取的存储数据相关配置,支持单个对象传入,同样也支持多个对象封装的数组格式

* @config {String} key 待存储数据的key

* @return {String} 本地存储的数据,传入为单个对象时,返回单个对象,获取不到时返回null;传入为数组时,返回为数组

*/

get : function(obj){

//获取某一个本地存储

var_get_ = function(config){

//结果

varresult = null;

if(typeofconfig === "string") config = {key : config};

//key校验

if(!_isValidKey(config.key)){return result;}

//支持本地存储的浏览器:IE8+、Firefox3.0+、Opera10.5+、Chrome4.0+、Safari4.0+、iPhone2.0+、Andrioid2.0+

if(_isSupportLocalStorage){

result= window.localStorage.getItem(config.key);

//过期时间判断,如果过期了,则移除该项

if(result){

varexpires = window.localStorage.getItem(config.key + ".expires");

result= {

value: result,

expires: expires ? new Date(expires) : null

};

if(result&& result.expires && result.expires < new Date()) {

result= null;

window.localStorage.removeItem(config.key);

window.localStorage.removeItem(config.key + ".expires");

}

}

}else if(_isSupportUserData) { //IE7及以下版本,采用UserData方式

//这里不用单独判断其expires,因为UserData本身具有这个判断

result= _getItem(config);

if(result){

result= { value : result };

}

}

returnresult ? result.value : null;

};

varrst = null;

//判断传入的参数是否为数组

if(obj&& obj.constructor === Array && obj instanceof Array){

rst= [];

for(vari = 0,len = obj.length;i < len;i++){

rst.push(_get_(obj[i]));

}

}elseif(obj){

rst= _get_(obj);

}

returnrst;

},

/**

* 移除某一项本地存储的数据

* <pre><code>

* //删除一个本地存储项

* qext.LocalStorage.remove({

* key: "username"

* });

* //删除多个本地存储项目 *

* qext.LocalStorage.remove([{

* key: "username"

* },{

* key: "password"

* },{

* key: "sex"

* }]);

* </code></pre>

* @param {String} obj 待移除的存储数据相关配置,支持移除某一个本地存储,也支持数组形式的批量移除

* @config {String} key 待移除数据的key

* @return 无

*/

remove : function(obj){

//移除某一项本地存储的数据

var_remove_ = function(config){

//支持本地存储的浏览器:IE8+、Firefox3.0+、Opera10.5+、Chrome4.0+、Safari4.0+、iPhone2.0+、Andrioid2.0+

if(_isSupportLocalStorage){

window.localStorage.removeItem(config.key);

window.localStorage.removeItem(config.key+ ".expires");

}else if(_isSupportUserData){ //IE7及以下版本,采用UserData方式

_removeItem(config);

}

};

//判断传入的参数是否为数组

if(obj&& obj.constructor === Array && obj instanceof Array){

for(vari = 0,len = obj.length;i < len;i++){

_remove_(obj[i]);

}

}elseif(obj){

_remove_(obj);

}

},

/**

* 清除所有本地存储的数据

* <pre><code>

* qext.LocalStorage.clearAll();

* </code></pre>

*/

clearAll : function(){

//支持本地存储的浏览器:IE8+、Firefox3.0+、Opera10.5+、Chrome4.0+、Safari4.0+、iPhone2.0+、Andrioid2.0+

if(_isSupportLocalStorage) {

window.localStorage.clear();

} else if(_isSupportUserData) { //IE7及以下版本,采用UserData方式

_clearAll();

}

},

//保存单个对象到本地

save:function(StudentID,ExamID,QuestionID,ExamAnswer){

qext.LocalStorage.set({

key : StudentID+ExamID+QuestionID,

value : "{ StudentID’: ‘"+StudentID+"’,‘ExamID’: ‘"+ExamID+"’, ‘QuestionID’:‘"+QuestionID+"’,‘ExamAnswer’: ‘"+ExamAnswer+"’}",

expires : 3600 * 1000 /*单位:ms*/

});

},

/**

* 获取所有的本地存储数据对应的key

* <pre><code>

* var keys = qext.LocalStorage.getAllKeys();

* </code></pre>

* @return {Array} 所有的key

*/

getAllKeys : function(){

var result = [];

//支持本地存储的浏览器:IE8+、Firefox3.0+、Opera10.5+、Chrome4.0+、Safari4.0+、iPhone2.0+、Andrioid2.0+

if(_isSupportLocalStorage) {

var key;

for(var i = 0,len =window.localStorage.length;i < len;i++){

key = window.localStorage.key(i);

if(!/.+\.expires$/.test(key)) {

result.push(key);

}

}

} else if(_isSupportUserData) { //IE7及以下版本,采用UserData方式

result = _getAllKeys();

}

return result;

}

};

})();

挺佩服封装这个代码的人,感觉无论从编程习惯还是代码的质量上都值得每一个人学习,所以,推荐一下。

到此,所有的本地存储的问题都解决了,又是完美的一天!

下面是一个完整的程序:我是完整的程序

参考博客:http://www.cnblogs.com/xiaowei0705/archive/2011/04/19/2021372.html
http://www.baidufe.com/item/af0bb5872f2a1ef337ce.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: