兼容多浏览器的本地存储

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

直接读取XML实现方式

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



    但是这样写虽然简单,带来的弊端也不少,于是上网查到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下使用,所以也实现了一下:









HTML5 LocalStorage   

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

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

兼容多浏览器的本地存储_第1张图片

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

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

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

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

兼容多浏览器的本地存储_第2张图片

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

    下面写一个简单的实例:


You have viewed this page 0 time(s).



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

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

兼容多浏览器的本地存储_第3张图片

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

我是借鉴的代码哦

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

完整的demo

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





 
无标题文档
 
   
 
 
 


 








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 ([email protected])
 */
(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,
       
       /**
        * 将数据进行本地存储(只能存储字符串信息)
        * 

                    * //保存单个对象
                    * 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
                    * }]);
        * 
* @param {Object} obj 待存储数据相关配置,可以是单个JSON对象,也可以是由多个JSON对象组成的数组 *
    *
  • key : String 待存储数据的key,务必将key值起的复杂一些,如:baidu.username
*
  • value : String 待存储数据的内容
  • *
  • expires : String/Object (Optional)数据的过期时间,可以是数字,单位是毫秒;也可以是日期对象,表示过期时间
  • * */ 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); } }, /** * 提取本地存储的数据 *
    
                        * //获取某一个本地存储,返回值为:{key:"",value:"",expires:""},未取到值时返回值为:null
                        * var rst = qext.LocalStorage.get({
                        *             key: "username"
                        * });
                        * //获取多个本地存储,返回值为:["","",""],未取到值时返回值为:[null,null,null]
                        * qext.LocalStorage.get([{
                        *             key: "username"
                        * },{
                        *             key: "password"
                        * },{
                        *             key: "sex"
                        * }]);
            * 
    * @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; }, /** * 移除某一项本地存储的数据 *
    
                        * //删除一个本地存储项
                        * qext.LocalStorage.remove({
                        *             key: "username"
                        * });
                        * //删除多个本地存储项目 *
                        * qext.LocalStorage.remove([{
                        *             key: "username"
                        * },{
                        *             key: "password"
                        * },{
                        *             key: "sex"
                        * }]);
            * 
    * @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); } }, /** * 清除所有本地存储的数据 *
    
            * qext.LocalStorage.clearAll();
            * 
    */ 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 *
    
            * var keys = qext.LocalStorage.getAllKeys();
            * 
    * @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


    你可能感兴趣的:(J2EE)