2013年11月7日 星期四

[轉貼] Web Storage - 可讓網頁將資料儲存於本地端的技術

出處:http://huan-lin.blogspot.com/2012/06/html5-web-storage.html

HTML5 Web Storage

摘要:這篇文章會涵蓋 HTML5 Web Storage 的幾個重點觀念,並介紹其基本用法,以及一些該注意的地方(包含一個使用 Visual Studio 2012 來撰寫範例程式的短片)。

Web Storage 要點整理
  • HTML5 的 Web Storage 是一種可讓網頁將資料儲存於本地端的技術,其作用如同 cookie。
  • 儲存於 Web Storage 中的資料,是以 key-value pair 的形式保存(如同 cookie)。
  • Cookie 儲存空間很小,最多僅能儲存 4 KB 的資料。HTML5 Web Storage 的儲存空間則大得多,且依各家瀏覽器的實作而不同。一般應該至少有 5 MB 的空間。  
  • 儲存於 cookie 中的資料會在用戶端瀏覽器與伺服器之間旅行(每次瀏覽器送出 request 至伺服器時就會夾帶 cookies),Web Storage 則不會(純粹運作於用戶端)。這表示 Web Storage 不會占用網路頻寬。
  • Web Storage 分為兩種:local storage 和 session storage。細節稍後會說明。

Web Storage 有兩種

Web Storage 分為兩種:local storage 和 session storage。二者的主要差異在於壽命長短與有效範圍。
壽命長短:儲存於 local storage 中的資料,其生命週期較長,session storage 則較短,只要瀏覽器視窗或分頁(tab)關閉就會消失。
有效範圍:儲存於 local storage 的資料可以跨瀏覽器分頁(tab),session storage 則不行。
先知道這樣就好,稍後會進一步說明,並且用一個影片來展示它們的差別。
儲存與讀取

儲存資料的時候,是用 Storage 物件的 setItem 方法。這裡的 Storage 物件,指的是 localStorage 或 sessionStorage,看你想要使用哪一個儲存空間。

範例:
?
1
2
window.localStorage.setItem("MyKeyName", "MyDataValue");
window.sessionStorage.setItem("MyKeyName", "MyDataValue");

從 Storage 中讀取資料時,則用 getItem 方法:
?
1
2
var value1 = window.localStorage.getItem("MyKeyName");
var value2 = window.sessionStorage.getItem("MyKeyName");

在使用 Storage 物件時,前面的 "window" 也可以省略不寫,而且還可以用陣列索引以及屬性的寫法,因此底下的程式片段的每一行作用皆相同:
?
1
2
3
4
window.localStorage.setItem("MyKeyName", "MyDataValue");
localStorage.setItem("MyKeyName", "MyDataValue");
localStorage["MyKeyName"] = "Hello";
localStorage.MyKeyName = "Hello";

儲存在 Web Storage 裡面的資料都可以跨頁面,也就是說,使用者點進去某個網頁之後,先前由上一個網頁所儲存於 Web Storage 的資料,都可以在後續的網頁中取得。
注意:有些瀏覽器可能允許你存入字串之外的型別,但 HTML5 的標準是只能存入字串。
清除

呼叫 removeItem 方法可以移除某一筆資料,例如:
?
1
localStorage.removeItem("MyKeyName");
如果要清除 Storage 物件中的全部資料,可用 clear 方法。
實作練習

底下的短片,主要目的是展示 localStorage 和 sessionStorage 的差異。


範例程式碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<head>
    <title></title>
    <script src="Scripts/modernizr-2.5.3.js" type="text/javascript"></script>
    <script type="text/javascript">
        function onLoad() {
            outputArea.value = window.localStorage.remainingSpace;
 
            btnSave.addEventListener("click", saveToStorage);
            btnLoadFromLocalStorage.addEventListener("click", loadFromLocalStorage);
            btnLoadFromSessionStorage.addEventListener("click", loadFromSessionStorage);
        }
 
        function saveToStorage() {
            //window.localStorage.setItem("UserData", inputArea.value);
            //localStorage.setItem("UserData", inputArea.value);
            //localStorage["UserData"] = inputArea.value;
            localStorage.UserData = inputArea.value;
            sessionStorage.UserData = inputArea.value;
        }
 
        function loadFromLocalStorage() {
            outputArea.value = localStorage["UserData"];
        }
 
        function loadFromSessionStorage() {
            outputArea.value = sessionStorage["UserData"];
        }
    </script>
</head>
<body onload="onLoad()">
    Input: <textarea id="inputArea"></textarea>
    Output: <textarea id="outputArea"></textarea>
     
 
 
    <button id="btnSave">儲存至 local 與 session storage</button>
    <button id="btnLoadFromLocalStorage">從 local storage 載入資料</button>
    <button id="btnLoadFromSessionStorage">從 session storage 載入</button>
</body>
</html>
Local Storage vs. Session Storage

經過前面的重點提示以及影片的範例展示,看官應該已經大致了解這種兩儲存空間的差別。這裡再稍微囉嗦一下。

有沒有碰過一種情形:你在 A 網頁選了某些選項或輸入資料,想說先不要 submit 到下一頁,而選擇將此頁再開一個到新視窗,然後在那個新視窗裡面輸入不同的資料,並且 submit 到下一頁,看看會產生甚麼結果。比如說,你可能只是想試試看不同的寄送方式,其運費的差異是多少。於是,你在第一個視窗裡面選了一般掛號,然後在另一個新視窗裡面挑選了快遞。了解價格差異之後,覺得快遞太貴了,還是用掛號就好,於是把選了快遞的那個新視窗關閉,回到你原先的那個視窗繼續操作,可是最後確認交易時,網頁送出的卻是「快遞」。 

如果使用 cookie 來保存使用者目前輸入的資料,寫程式時沒有特別注意,就有可能會發生上述情形。這是因為 cookie 會隨著 request 送到伺服器端,然後又隨著 response 送回前端瀏覽器的緣故(於是後來選擇的「快遞」隨著 response 帶回前端又蓋掉了先前儲存的 cookie 值)。在某些應用場合,這種錯誤可能會導致挺嚴重的後果。

Session storage 就可以解決上述問題,因為它的設計就是生命週期短--視窗或分頁關掉就沒了,而且有效範圍窄--資料無法跨分頁,所以無論是開新分頁或新視窗,在先前那個分頁中所儲存於 session storage 中的資料都不會「撈過界」。那麼,網頁 refresh(按 F5)之後,原先儲存在 session storage 中的資料呢?放心,都還在!
注意:有些瀏覽器可能會嘗試延長 session 的壽命。例如當某個分頁掛掉時,使用者選擇重新啟動瀏覽器。有些瀏覽器搞不好會先把這些 session storage 資料保存到暫存檔案,等到重新啟動時再 restore 這些資料。這只能算是特例啦。
至於 local storage,應該就很清楚了:適合用於資料需要跨分頁、跨視窗,甚至瀏覽器關掉再開都還要存在的場合。

隔離

此外,瀏覽器會把不同網站的 local storage 隔離開來(session storage 自是不在話下)。意思是,某個網站的網頁所儲存於 local storage 中的資料,其他網站的網頁都看不到。

說得更精確一點,local storage 的資料隔離,係依據「同源策略」。也就是說,只有源自相同網站的網頁才能共享同一塊 local storage。

那麼,怎樣才叫做「相同來源」的網頁呢?就是網址當中的協定、主機名稱、傳輸埠這三者都相同的,即視為相同來源。
注意:不同協定即視為不同來源,所以如果你的網站同時提供 http 和 https,你知道這意味著什麼吧?
注意:不同主機名稱(host name)即視為不同來源,所以相同主機名稱底下的虛擬路徑,是屬於相同來源。 
補充事項
  • 在 HTML5 眾多技術區塊當中,Web Storage 是可以放心使用的,因為現在幾乎所有的瀏覽器都有實作這項功能了(IE 8 也有支援)。
  • 雖然 IE 10 有實作 Web Storage 物件的 remainingSpace 屬性,但 Visual Studio 2012 (RC 版)的 HTML 編輯器的 IntelliSense 功能並不會出現提示(如前面的影片中所展示的)。
  • Chrome v19.0.x 不支援 remainingSpace 屬性。

最後,Chrome 瀏覽器的「開發人員工具」可以讓你查看、修改、和刪除 Web Storage 資料,參考下圖:


小結

原本只是想用幾個小黑點,重點條列 Web Storage 的幾個特性而已,沒想到寫了這麼落落長,還弄了個影片 XD

不過,還是有些沒提到的地方,例如 Web Storage 的事件處理。

延伸閱讀

2013年11月3日 星期日

[轉貼] 利用DotRAS組件,實現ADSL的自動撥號斷網自動化操作

出處:http://www.cnblogs.com/wuhuacong/archive/2010/09/06/1819612.html

有些場合,爲了避免服務對用戶IP的限制或者爲了用戶的方便,可以通過代碼實現自動化的撥號或者斷網操作,通過DotRAS組件,可以非常方便的實現如ADSL、VPN等撥號以及相關操作,DotRAS組件是專門提供這樣遠程訪問服務的模塊,本文介紹如何通過應用該組件,實現ADSL網絡的撥號、斷網、獲取用戶IP的操作。
DotRAS組件的項目地址是:http://dotras.codeplex.com/  
先看看Demo的界面效果
 
具體的代碼邏輯,是通過列出電話簿裏面的撥號連接,設置是通過賬戶密碼或者默認賬戶設置信息,進行撥號即可,使用DotRas組件,使得在DotNet中操作這些功能非常方便,代碼貼上如下所示:
複制代碼
        /// <summary>
        
/// 測試撥號連接
        
/// </summary>
        private void btnTest_Click(object sender, EventArgs e)
        {
            
try
            {
                RasDialer dialer 
= new RasDialer();
                dialer.EntryName 
= "聯通網絡";
                dialer.PhoneNumber 
= " ";
                dialer.AllowUseStoredCredentials 
= true;
                dialer.PhoneBookPath 
= RasPhoneBook.GetPhoneBookPath(RasPhoneBookType.AllUsers);
                dialer.Timeout 
= 1000;
                dialer.Dial();

                Thread.Sleep(
100);
                
this.LoadConnections();
            }
            
catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        
/// <summary>
        
/// 斷開網絡連接
        
/// </summary>
        private void btnLogout_Click(object sender, EventArgs e)
        {
            ReadOnlyCollection
<RasConnection> conList = RasConnection.GetActiveConnections();
            
foreach (RasConnection con in conList)
            {
                con.HangUp();
            }

            
this.LoadConnections();
        }

        
private void Form1_Load(object sender, EventArgs e)
        {
             
this.LoadConnections();
        }

        
/// <summary>
        
/// 顯示活動的連接
        
/// </summary>
        private void LoadConnections()
        {
            
this.comboBox1.Items.Clear();
            
this.comboBox1.Items.Add(new ComboBoxItem("請選擇一個鏈接..."null));
            
foreach (RasConnection connection in RasConnection.GetActiveConnections())
            {
                
this.comboBox1.Items.Add(new ComboBoxItem(connection.EntryName, connection.EntryId));
            }

            
this.comboBox1.SelectedIndex = 0;
        }

        
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            
this.GetAddressButton.Enabled = this.comboBox1.SelectedIndex > 0;
        }

        
/// <summary>
        
/// 獲取IP地址信息
        
/// </summary>
        private void GetAddressButton_Click(object sender, EventArgs e)
        {
            StringBuilder sb 
= new StringBuilder();
            
foreach (RasConnection connection in RasConnection.GetActiveConnections())
            {
                
if (connection.EntryId == (Guid)((ComboBoxItem)this.comboBox1.SelectedItem).Value)
                {
                    RasIPInfo ipAddresses 
= (RasIPInfo)connection.GetProjectionInfo(RasProjectionType.IP);
                    
if (ipAddresses != null)
                    {
                        sb.AppendFormat(
"ClientIP:{0}\r\n", ipAddresses.IPAddress.ToString());
                        sb.AppendFormat(
"ServerIP:{0}\r\n", ipAddresses.ServerIPAddress.ToString());
                    }
                }
                sb.AppendLine();
            }
            MessageBox.Show(sb.ToString());
        }
複制代碼


通過以上的代碼,可以非常方便實現寬帶的撥號連接和獲取IP等設置,不過斷網之後,一般的IP還是和原來一樣,這個可能和服務器的緩存有關系,爲了實現撥號後,本地爲不同的IP設置,需要特別的處理才可以。 

[轉貼] 獲取本機電腦名稱,IP,MAC地址,硬碟ID

出處:http://www.cnblogs.com/qixuejia/archive/2011/01/12/1933442.html


using System;
using System.Management;

public class ComputerInfo
{
    public string CpuID;
    public string MacAddress;
    public string DiskID;
    public string IpAddress;
    public string LoginUserName;
    public string ComputerName;
    public string SystemType;
    public string TotalPhysicalMemory; //單位:M
    private static ComputerInfo _instance;

    public static ComputerInfo Instance()
    {
        if (_instance == null)
            _instance = new ComputerInfo();
        return _instance;
    }

    public ComputerInfo()
    {
        CpuID = GetCpuID();
        MacAddress = GetMacAddress();
        DiskID = GetDiskID();
        IpAddress = GetIPAddress();
        LoginUserName = GetUserName();
        SystemType = GetSystemType();
        TotalPhysicalMemory = GetTotalPhysicalMemory();
        ComputerName = GetComputerName();
    }

    //獲取CPU序列號
    public string GetCpuID()
    {
        try
        {
            string cpuInfo = "";
            ManagementClass mc = new ManagementClass("Win32_Processor");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {
                cpuInfo = mo.Properties["ProcessorId"].Value.ToString();
            }
            moc = null;
            mc = null;
            return cpuInfo;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //獲取網卡硬件地址
    public string GetMacAddress()
    {
        try
        {
            string mac = "";
            ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {
                if ((bool)mo["IPEnabled"] == true)
                {
                    mac = mo["MacAddress"].ToString();
                    break;
                }
            }
            moc = null;
            mc = null;
            return mac;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //獲取IP地址
    public string GetIPAddress()
    {
        try
        {
            string st = "";
            ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {
                if ((bool)mo["IPEnabled"] == true)
                {
                    //st=mo["IpAddress"].ToString();
                    System.Array ar;
                    ar = (System.Array)(mo.Properties["IpAddress"].Value);
                    st = ar.GetValue(0).ToString();
                    break;
                }
            }
            moc = null;
            mc = null;
            return st;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //獲取硬盤ID
    public string GetDiskID()
    {
        try
        {
            String HDid = "";
            ManagementClass mc = new ManagementClass("Win32_DiskDrive");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {
                HDid = (string)mo.Properties["Model"].Value;
            }
            moc = null;
            mc = null;
            return HDid;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //操作系統的登錄用戶名
    public string GetUserName()
    {
        try
        {
            string st = "";
            ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {

                st = mo["UserName"].ToString();

            }
            moc = null;
            mc = null;
            return st;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //PC類型
    public string GetSystemType()
    {
        try
        {
            string st = "";
            ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {
                st = mo["SystemType"].ToString();
            }
            moc = null;
            mc = null;
            return st;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //物理內存
    public string GetTotalPhysicalMemory()
    {
        try
        {
            string st = "";
            ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach (ManagementObject mo in moc)
            {
                st = mo["TotalPhysicalMemory"].ToString();
            }
            moc = null;
            mc = null;
            return st;
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }

    //電腦名稱
    public string GetComputerName()
    {
        try
        {
            return System.Environment.GetEnvironmentVariable("ComputerName");
        }
        catch
        {
            return "unknow";
        }
        finally
        {
        }
    }
}

2013年11月1日 星期五

[轉貼] 獲取網頁中的驗證碼圖片

出處:http://www.cnblogs.com/hobe/archive/2007/03/14/674292.html

有時候我們需要獲得網頁上的圖片,尤其是向驗證碼這樣的圖片.這個方法就是將網頁上的圖片獲取到PictureBox中.效果入下圖所示.


右邊是使用Webbrowser控件裝載的某網站的注冊頁面,其中包括了驗證碼.左邊是獲取到的驗證碼,裝載在PictureBox中.也許有人會問,通過Webbrowser也能夠看到注冊頁面的驗證碼為什麼還要,在獲得這個驗證碼.原因如下:當你不想讓別人知道在做什麼的時候需要使用,別人只能看到注冊碼而不知道在干什麼事情;另外願意是為了方便,當做這個一個注冊程序的時候,注冊信息一般都是自動生成的,但是驗證碼需要輸入,不停的拖動滾動條找注冊碼的位置不方便.

下面看看如何實現.

首先需要添加mshtml的引用,之後using mshtml;
        public static Image GetRegCodePic(WebBrowser wbMail, string ImgName, string Src, string Alt)
        
{
            HTMLDocument doc 
= (HTMLDocument)wbMail.Document.DomDocument;
            HTMLBody body 
= (HTMLBody)doc.body;
            IHTMLControlRange rang 
= (IHTMLControlRange)body.createControlRange();
            IHTMLControlElement Img;
            
if (ImgName == ""//如果沒有圖片的名字,通過Src或Alt中的關鍵字來取
            {
                
int ImgNum = GetPicIndex(wbMail, Src,Alt);
                
if (ImgNum == -1return null;
                Img 
= (IHTMLControlElement)wbMail.Document.Images[ImgNum].DomElement;
            }

            
else
                Img 
= (IHTMLControlElement)wbMail.Document.All[ImgName].DomElement;

            rang.add(Img);
            rang.execCommand(
"Copy"falsenull);
            Image RegImg 
= Clipboard.GetImage();
            Clipboard.Clear();
            
return RegImg;
        }


        
public static int GetPicIndex(WebBrowser wbMail, string Src, string Alt)
        
{
            
int imgnum = -1;
            
for (int i = 0; i < wbMail.Document.Images.Count; i++) //獲取所有的Image元素
            {
                IHTMLImgElement img 
= (IHTMLImgElement)wbMail.Document.Images[i].DomElement;
                
if (Alt == "")
                
{
                    
if (img.src.Contains(Src)) return i;
                }

                
else
                
{
                    
if (!string.IsNullOrEmpty(img.alt))
                    
{
                        
if (img.alt.Contains(Alt)) return i;
                    }

                }

            }

            
return imgnum;
        }



通過調用GetRegCodePic就可以獲得注冊碼圖片了.下面是幾個示例.
示例1:
下面是某個站的注冊碼圖片的HTML部分源代碼

<IMG height=80 alt="Registration Verification Code" src="......" width=290 border=0>

picturebox1.Image =GetRegCodePic(wbMail, "", "", "Registration Verification Code")


示例2:
下面是某個站的注冊碼圖片的HTML部分源代碼

<IMG id=CAPTCHAImage src="......." name=CAPTCHAImage>

picturebox1.Image =GetRegCodePic(wbMail, "CAPTCHAImage", "", "")  //通過驗證碼Html元素的名字來取