數據存儲

隨着Web應用程序的出現,產生了對於可以直接在客戶端上存儲用戶信息能力的要求。好比登陸信息、偏好設定或其餘數據,這個問題的第一個方案是以cookie的形式出現的,今天cookie只是在客戶端存儲數據的其中一種選項。html

cookie

HTTP Cookie,一般直接叫作cookie,最初是在客戶端用於存儲會話信息的。該標準要求服務器對任意HTTP請求發送Set-Cookie HTTP頭做爲響應的一部分,其中包含會話信息。例如,這種服務器響應的頭可能以下:web

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value

這個HTTP響應設置以name爲名稱、以value爲值的一個cookie,名稱和值在傳送時都必須是URL編碼的。瀏覽器會存儲這樣的會話信息,並在這以後,經過爲每一個請求添加Cookie HTTP頭將信息發送回服務器,以下所示:windows

GET /index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value

發送回服務器的額外信息能夠用於惟一驗證客服來自於發送的哪一個請求。瀏覽器

限制緩存

cookie在性質上是綁定在特定的域名下的。當設定了一個cookie後,再給建立它的域名發送請求時,都會包含這個cookie。這個限制確保了存儲在cookie中的信息只能讓批准的接受者訪問,而沒法被其餘域訪問。因爲cookie是存在客戶端計算機上的,還加入了一些限制確保cookie不會被惡意使用,同時不會佔據太多磁盤空間。每一個域的cookie總數是有限的,不過瀏覽器之間各有不一樣。以下所示:安全

IE6以及更低版本限制每一個域名最多20個cookie。
IE7和以後版本每一個域名最多50個。IE7最初是支持每一個域名最大20個cookie以後被微軟的一個補丁所更新。
Firefox限制每一個域最多50個cookie。
Opera限制每一個域最多30個cookie。
Safari和Chrome對於每一個域的cookie數量限制沒有硬性規定。

當超過單個域名限制以後還要再設置cookie,瀏覽器就會清除之前設置的cookie。IE和Opera會刪除最近最少使用過的(LRU,Least Recently Used)cookie,騰出空間給新設置的cookie。Firefox看上去好像是隨機決定要清除哪一個cookie,因此考慮cookie限制很是重要,以避免出現不可預期的後果。
瀏覽器中對呀cookie的尺寸也有限制。大多數瀏覽器都有大約4095B(含4095)之內。尺寸限制影響到一個域下全部的cookie,而並不是每一個cookie單獨限制。
若是你嘗試建立超過最大尺寸限制的cookie,那麼該cookie會被悄無聲息地丟掉。注意,雖然一個字符一般佔用一字節,可是多字節狀況則有所不一樣。服務器

cookie的構成cookie

cookie由瀏覽器保存的如下幾塊信息構成。session

名稱:一個惟一肯定cookie的名稱。cookie名稱是不區分大小寫的,因此myCookie和MyCookie被認爲是同一個cookie。然而,實踐中最好將cookie名稱看做是區分大小寫的,由於某些服務器會這樣處理cookie。cookie的名稱必須是通過URL編碼的。
值:儲存在cookie中的字符串值。值必須被URL編碼。
域:cookie對於哪一個域是有效的。全部向該域發送的請求中都會包含這個cookie信息。這個值能夠包含子域(subdomain,如www.wrox.com),也能夠不包含它(如.wrox.com,則對於wrox.com的全部子域都有效)。若是沒有明確設定,那麼這個域會被認做來自設置 cookie 的那個域。
路徑:對於指定域中的那個路徑,應該向服務器發送 cookie。例如,你能夠指定 cookie 只有從http://www.wrox.com/books/ 中才能訪問,那麼 http://www.wrox.com 的頁面就不會發送 cookie 信息,即便請求都是來自同一個域的。
失效時間:表示 cookie 什麼時候應該被刪除的時間戳(也就是,什麼時候應該中止向服務器發送這個cookie)。默認狀況下,瀏覽器會話結束時即將全部 cookie 刪除;
         不過也能夠本身設置刪除時間。這個值是個 GMT 格式的日期(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用於指定應該刪除cookie 的準確時間。所以,cookie 可在瀏覽器關閉後依然保存在用戶的機器上。若是你設置的失效日期是個之前的時間,則 cookie 會被馬上刪除。
安全標誌:指定後,cookie 只有在使用 SSL 鏈接的時候才發送到服務器。例如,cookie 信息只能發送給 https://www.wrox.com,而 http://www.wrox.com 的請求則不能發送 cookie。

每一段信息都做爲 Set-Cookie 頭的一部分,使用分號加空格分隔每一段,以下例所示。dom

HTTP/1.1 200 OK 
Content-type: text/html 
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com 
Other-header: other-header-value

該頭信息指定了一個叫作 name 的 cookie,它會在格林威治時間 2007 年 1 月 22 日 7:10:24 失效,同時對於 www.wrox.com 和 wrox.com 的任何子域(如 p2p.wrox.com)都有效。

secure 標誌是 cookie 中惟一一個非名值對兒的部分,直接包含一個 secure 單詞。以下:
HTTP/1.1 200 OK 
Content-type: text/html 
Set-Cookie: name=value; domain=.wrox.com; path=/; secure 
Other-header: other-header-value

這裏,建立了一個對於全部 wrox.com 的子域和域名下(由 path 參數指定的)全部頁面都有效的cookie。由於設置了 secure 標誌,這個 cookie 只能經過 SSL 鏈接才能傳輸。
尤爲要注意,域、路徑、失效時間和 secure 標誌都是服務器給瀏覽器的指示,以指定什麼時候應該發送 cookie。這些參數並不會做爲發送到服務器的 cookie 信息的一部分,只有名值對兒纔會被髮送。

JavaScript 中的 cookie

在JavaScript中處理cookie有些複雜,由於其衆所周知的蹩腳的接口,即BOM的document. cookie屬性。這個屬性的獨特之處在於它會由於使用它的方式不一樣而表現出不一樣的行爲。當用來獲取屬性值時,document.cookie 返回當前頁面可用的(根據 cookie 的域、路徑、失效時間和安全設置)全部 cookie的字符串,一系列由分號隔開的名值對兒,以下例所示。

name1=value1;name2=value2;name3=value3

**全部名字和值都是通過 URL 編碼的,因此必須使用 decodeURIComponent()來解碼。
當用於設置值的時候,document.cookie 屬性能夠設置爲一個新的 cookie 字符串。這個 cookie 字符串會被解釋並添加到現有的 cookie 集合中。設置 document.cookie 並不會覆蓋 cookie,除非設置的cookie 的名稱已經存在。設置 cookie 的格式以下,和 Set-Cookie 頭中使用的格式同樣。**

name=value; expires=expiration_time; path=domain_path; domain=domain_name; secure

這些參數中,只有 cookie 的名字和值是必需的。下面是一個簡單的例子。

document.cookie = "name=Nicholas";

這段代碼建立了一個叫 name 的 cookie,值爲 Nicholas。當客戶端每次向服務器端發送請求的時候,都會發送這個 cookie;當瀏覽器關閉的時候,它就會被刪除。雖然這段代碼沒問題,但由於這裏正好名稱和值都無需編碼,因此最好每次設置 cookie 時都像下面這個例子中同樣使用 encodeURIComponent()。

document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas");

要給被建立的 cookie 指定額外的信息,只要將參數追加到該字符串,和 Set-Cookie 頭中的格式同樣,以下所示。

document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas") + "; domain=.wrox.com; path=/";

因爲 JavaScript 中讀寫 cookie 不是很是直觀,經常須要寫一些函數來簡化 cookie 的功能。基本的cookie操做有三種:讀取、寫入和刪除。它們在 CookieUtil 對象中以下表示。

var CookieUtil = {
    get: function (name) {
        var cookieName = encodeURIComponent(name) + '=',
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null;
        if (cookieStart > -1) {
            var cookieEnd =   document.cookie.indexOf(";", cookieStart); 
            if (cookieEnd == -1){ 
                cookieEnd = document.cookie.length; 
            } 
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart 
            + cookieName.length, cookieEnd));
        }
        return cookieValue;
    },
    set: function (name, value, expires, path, domain, secure) { 
        var cookieText = encodeURIComponent(name) + "=" + 
                         encodeURIComponent(value); 
        if (expires instanceof Date) { 
            cookieText += "; expires=" + expires.toGMTString(); 
        } 
        if (path) { 
            cookieText += "; path=" + path; 
        }
        if (domain) { 
            cookieText += "; domain=" + domain; 
        } 
        if (secure) { 
            cookieText += "; secure"; 
        } 
        document.cookie = cookieText; 
    },
    unset: function (name, path, domain, secure){ 
         this.set(name, "", new Date(0), path, domain, secure); 
     }
}

CookieUtil.get()方法根據 cookie 的名字獲取相應的值。它會在 document.cookie 字符串中查找 cookie 名加上等於號的位置。若是找到了,那麼使用 indexOf()查找該位置以後的第一個分號(表示了該 cookie 的結束位置)。若是沒有找到分號,則表示該 cookie 是字符串中的最後一個,則餘下的字符串都是 cookie 的值。該值使用 decodeURIComponent()進行解碼並最後返回。若是沒有發現 cookie,則返回 null。
CookieUtil.set()方法在頁面上設置一個 cookie,接收以下幾個參數:cookie 的名稱,cookie 的值,可選的用於指定 cookie 什麼時候應被刪除的 Date 對象,cookie 的可選的 URL 路徑,可選的域,以及可選的表示是否要添加 secure 標誌的布爾值。參數是按照它們的使用頻率排列的,只有頭兩個是必需的。在這個方法中,名稱和值都使用encodeURIComponent()進行了URL編碼,並檢查其餘選項。若是expires參數是 Date 對象,那麼會使用 Date 對象的 toGMTString()方法正確格式化 Date 對象,並添加到expires 選項上。方法的其餘部分就是構造 cookie 字符串並將其設置到 document.cookie 中。沒有刪除已有 cookie 的直接方法。因此,須要使用相同的路徑、域和安全選項再次設置 cookie,並將失效時間設置爲過去的時間。CookieUtil.unset()方法能夠處理這種事情。它接收 4 個參數:要刪除的 cookie 的名稱、可選的路徑參數、可選的域參數和可選的安全參數。這些參數加上空字符串並設置失效時間爲 1970 年 1 月 1 日(初始化爲 0ms 的 Date 對象的值),傳給 CookieUtil.set()。這樣就能確保刪除 cookie。能夠像下面這樣使用上述方法。

//設置 cookie 
CookieUtil.set("name", "Nicholas"); 
CookieUtil.set("book", "Professional JavaScript"); 
//讀取 cookie 的值
alert(CookieUtil.get("name")); //"Nicholas" 
alert(CookieUtil.get("book")); //"Professional JavaScript" 
//刪除 cookie 
CookieUtil.unset("name"); 
CookieUtil.unset("book"); 
//設置 cookie,包括它的路徑、域、失效日期
CookieUtil.set("name", "Nicholas", "/books/projs/", "www.wrox.com", 
 new Date("January 1, 2010")); 
//刪除剛剛設置的 cookie 
CookieUtil.unset("name", "/books/projs/", "www.wrox.com"); 
//設置安全的 cookie 
CookieUtil.set("name", "Nicholas", null, null, null, true); 
CookieExample01.htm

這些方法經過處理解析、構造 cookie 字符串的任務令在客戶端利用 cookie 存儲數據更加簡單。

關於 cookie 的思考

因爲全部的 cookie 都會由瀏覽器做爲請求頭髮送,因此在 cookie 中存儲大量信息會影響到特定域的請求性能。cookie 信息越大,完成對服務器請求的時間也就越長。儘管瀏覽器對 cookie 進行了大小限制,不過最好仍是儘量在 cookie 中少存儲信息,以免影響性能。cookie 的性質和它的侷限使得其並不能做爲存儲大量信息的理想手段,因此又出現了其餘方法。必定不要在 cookie 中存儲重要和敏感的數據。cookie 數據並不是存儲在一個安全環境中,其中包含的任何數據均可以被他人訪問。因此不要在 cookie 中存儲諸如信用卡號或者我的地址之類的數據。

cookie與session:

cookie:存在瀏覽器裏,容量有限,不安全(用戶和瀏覽器都能看到)
1.防篡改 2.加密
session:存在服務器,容量不用擔憂,安全(用戶看不到)

圖片描述

session分爲兩種:
經常使用的cookie-session 基於cookie實現
瀏覽器第一次訪問服務器的時候,服務器生成一個session的id,sess_id,服務器會把這個sess_id做爲一個cookie還給瀏覽器
第二次訪問服務器,瀏覽器帶着cookie訪問服務器,服務器藉助瀏覽器帶的sess_id來識別用戶。

Web存儲機制

Web Storage 最先是在 Web 超文本應用技術工做組(WHAT-WG)的 Web 應用 1.0 規範中描述的。這個規範的最初的工做最終成爲了 HTML5 的一部分。Web Storage 的目的是克服由 cookie 帶來的一些限制,當數據須要被嚴格控制在客戶端上時,無須持續地將數據發回服務器。Web Storage 的兩個主要目標是:

提供一種在 cookie 以外存儲會話數據的途徑;
提供一種存儲大量能夠跨會話存在的數據的機制。

**最初的 Web Storage 規範包含了兩種對象的定義:sessionStorage 和 globalStorage。這兩個對象在支持的瀏覽器中都是以 windows 對象屬性的形式存在的,支持這兩個屬性的瀏覽器包括 IE8+、Firefox 3.5+、Chrome 4+和 Opera 10.5+。
Firefox 2 和 3 基於早期規範的內容部分實現了 Web Storage,當時只實現了globalStorage,沒有實現 localStorage。**

Storage 類型

Storage 類型提供最大的存儲空間(因瀏覽器而異)來存儲名值對兒。Storage 的實例與其餘對象相似,有以下方法。

clear(): 刪除全部值;Firefox 中沒有實現 。
getItem(name):根據指定的名字 name 獲取對應的值。
key(index):得到 index 位置處的值的名字。
removeItem(name):刪除由 name 指定的名值對兒。
setItem(name, value):爲指定的 name 設置一個對應的值。

其中,getItem()、removeItem()和 setItem()方法能夠直接調用,也可經過 Storage 對象間接調用。由於每一個項目都是做爲屬性存儲在該對象上的,因此能夠經過點語法或者方括號語法訪問屬性來讀取值,設置也同樣,或者經過 delete 操做符進行刪除。不過,咱們還建議讀者使用方法而不是屬性來訪問數據,以避免某個鍵會意外重寫該對象上已經存在的成員。還可使用 length 屬性來判斷有多少名值對兒存放在 Storage 對象中。但沒法判斷對象中全部數據的大小,不過 IE8 提供了一個 remainingSpace 屬性,用於獲取還可使用的存儲空間的字節數。Storage 類型只能存儲字符串。非字符串的數據在存儲以前會被轉換成字符串。

sessionStorage 對象

sessionStorage 對象存儲特定於某個會話的數據,也就是該數據只保持到瀏覽器關閉。這個對象就像會話 cookie,也會在瀏覽器關閉後消失。存儲在 sessionStorage 中的數據能夠跨越頁面刷新而存在,同時若是瀏覽器支持,瀏覽器崩潰並重啓以後依然可用(Firefox 和 WebKit 都支持,IE 則不行)。由於 seesionStorage 對象綁定於某個服務器會話,因此當文件在本地運行的時候是不可用的。存儲在 sessionStorage 中的數據只能由最初給對象存儲數據的頁面訪問到,因此對多頁面應用有限制。
因爲 sessionStorage 對象實際上是 Storage 的一個實例,因此可使用setItem()或者直接設置新的屬性來存儲數據。下面是這兩種方法的例子。

//使用方法存儲數據
sessionStorage.setItem("name", "Nicholas"); 
//使用屬性存儲數據
sessionStorage.book = "Professional JavaScript";

不一樣瀏覽器寫入數據方面略有不一樣。Firefox 和 WebKit 實現了同步寫入,因此添加到存儲空間中的數據是馬上被提交的。而 IE 的實現則是異步寫入數據,因此在設置數據和將數據實際寫入磁盤之間可能有一些延遲。對於少許數據而言,這個差別是能夠忽略的。對於大量數據,你會發現 IE 要比其餘瀏覽器更快地恢復執行,由於它會跳過實際的磁盤寫入過程。在 IE8 中能夠強制把數據寫入磁盤:在設置新數據以前使用 begin()方法,而且在全部設置完成以後調用 commit()方法。看如下例子。

//只適用於 IE8
sessionStorage.begin(); 
sessionStorage.name = "Nicholas"; 
sessionStorage.book = "Professional JavaScript"; 
sessionStorage.commit();

這段代碼確保了 name 和 book 的值在調用 commit()以後馬上被寫入磁盤。調用 begin()是爲了確保在這段代碼執行的時候不會發生其餘磁盤寫入操做。對於少許數據而言,這個過程不是必需的;不過,對於大量數據(如文檔之類的)可能就要考慮這種事務形式的方法了。
sessionStorage 中有數據時,可使用 getItem()或者經過直接訪問屬性名來獲取數據。兩種方法的例子以下。

//使用方法讀取數據
var name = sessionStorage.getItem("name"); 
//使用屬性讀取數據
var book = sessionStorage.book;

還能夠經過結合 length 屬性和 key()方法來迭代 sessionStorage 中的值,以下所示。

for (var i=0, len = sessionStorage.length; i < len; i++){ 
 var key = sessionStorage.key(i); 
 var value = sessionStorage.getItem(key); 
 alert(key + "=" + value); 
}

它是這樣遍歷 sessionStorage 中的名值對兒的:首先經過 key()方法獲取指定位置上的名字,而後再經過 getItem()找出對應該名字的值。還可使用 for-in 循環來迭代 sessionStorage 中的值:

for (var key in sessionStorage){ 
 var value = sessionStorage.getItem(key); 
 alert(key + "=" + value); 
}

每次通過循環的時候,key 被設置爲 sessionStorage 中下一個名字,此時不會返回任何內置方法或 length 屬性。
要從 sessionStorage 中刪除數據,可使用 delete 操做符刪除對象屬性,也可調用removeItem()方法。如下是這些方法的例子。

//使用 delete 刪除一個值——在 WebKit 中無效
delete sessionStorage.name; 
//使用方法刪除一個值
sessionStorage.removeItem("book");

在撰寫本書時,delete 操做符在 WebKit 中沒法刪除數據,removeItem()則能夠在各類支持的瀏覽器中正確運行。
sessionStorage 對象應該主要用於僅針對會話的小段數據的存儲。若是須要跨越會話存儲數據,那麼 globalStorage 或者 localStorage 更爲合適。

localStorage 對象

因爲 localStorage 是 Storage 的實例,因此能夠像使用 sessionStorage 同樣來使用它。下面是一些例子。

//使用方法存儲數據
localStorage.setItem("name", "Nicholas"); 
//使用屬性存儲數據
localStorage.book = "Professional JavaScript"; 
//使用方法讀取數據
var name = localStorage.getItem("name"); 
//使用屬性讀取數據
var book = localStorage.book;

存儲在 localStorage 中的數據保留到經過 JavaScript 刪除或者是用戶清除瀏覽器緩存。
爲了兼容只支持 globalStorage 的瀏覽器,可使用如下函數。

function getLocalStorage(){ 
 if (typeof localStorage == "object"){ 
 return localStorage; 
 } else if (typeof globalStorage == "object"){ 
 return globalStorage[location.host]; 
 } else { 
 throw new Error("Local storage not available."); 
 } 
}

而後,像下面這樣調用一次這個函數,就能夠正常地讀寫數據了。
var storage = getLocalStorage();

在肯定了使用哪一個 Storage 對象以後,就能在全部支持 Web Storage 的瀏覽器中使用相同的存取規則操做數據了。

storage 事件

對 Storage 對象進行任何修改,都會在文檔上觸發 storage 事件。當經過屬性或 setItem()方法保存數據,使用 delete 操做符或 removeItem()刪除數據,或者調用 clear()方法時,都會發生該事件。這個事件的 event 對象有如下屬性。

domain:發生變化的存儲空間的域名。
key:設置或者刪除的鍵名。
newValue:若是是設置值,則是新值;若是是刪除鍵,則是 null。
oldValue:鍵被更改以前的值。

在這四個屬性中,IE8 和 Firefox 只實現了 domain 屬性。在撰寫本書的時候,WebKit 尚不支持
storage 事件:
如下代碼展現瞭如何偵聽 storage 事件:

EventUtil.addHandler(document, "storage", function(event){ 
     alert("Storage changed for " + event.domain); 
});

不管對 sessionStorage、globalStorage 仍是 localStorage 進行操做,都會觸發 storage事件,但不做區分。

限制

與其餘客戶端數據存儲方案相似,Web Storage 一樣也有限制。這些限制因瀏覽器而異。通常來講,對存儲空間大小的限制都是以每一個來源(協議、域和端口)爲單位的。換句話說,每一個來源都有固定大小的空間用於保存本身的數據。考慮到這個限制,就要注意分析和控制每一個來源中有多少頁面須要保存數據。
對於 localStorage 而言,大多數桌面瀏覽器會設置每一個來源 5MB 的限制。Chrome 和 Safari 對每一個來源的限制是 2.5MB。而 iOS 版 Safari 和 Android 版 WebKit 的限制也是 2.5MB。
對 sessionStorage 的限制也是因瀏覽器而異。有的瀏覽器對 sessionStorage 的大小沒有限制,但 Chrome、Safari、iOS 版 Safari 和 Android 版 WebKit 都有限制,也都是 2.5MB。IE8+和 Opera 對sessionStorage 的限制是 5MB。
有關 Web Storage 的限制,請參考 http://dev-test.nemikor.com/w...

相關文章
相關標籤/搜索