Windows Azure 儲存體–Blob
在Desktop的環境裡面有硬碟可以用來儲存照片、影片、MP3等等,那Windows Azure當然也要儲存體來進行儲存啦,所以這次介紹的是Windows Azure專門用來存取大型資料的儲存體Blob(Binary Large Object),而在談論之前,也有一個觀念要給大家,無論是Blob或是Table、Queue,其實都是存在於資料庫裏面,所以本機要模擬的時候,也必須要有資料庫才能進行測試喔!
Blob架構
既然Blob是儲存在雲端上,就必須符合雲端的特性,所以架構必定和本機上有所不同,以下是Blob的結構圖,我們可以看到,基本上會分成三層,Account(帳號)、Container(容器)、Blob;畢竟網路那麼多使用者,所以一定是用Account來做區分,而一個Account會有非常多的Container,這些Container是可以自己去自訂的,Container的目的其實就像是利用群組的方式,來設定存取的權限,而Container裡面會有許多Blob,基本上就如下圖。( 其實Container也是物件,官方網站也稱Container為Blob Container ),此外,除了下圖外,Container和Blob還擁有Metadata可以進行描述。
Storage Account
- 所有的Blob是透過此帳號進行存取。
- 他是整個架構的起點。
- 一個帳號可以有非常多的Container。
Blog Container
- 此Container必須在Account內,且此Container幫Blob分了群組。
- 共享權限的等級,是設在Container裡面,目前支援Public Read和Private,當此權限為Public Read時,任何人都可以進行讀取,而不需要驗證,只有驗證過的對應使用者能進行存取。
- 容器也有Metadata,大小為8k,是一組<name,value>的形式。
- Blob Containet擁有可以列出所有Blob的方法,所以可以很輕鬆地去做存取。
Blob
- Blob儲存在Container裡面,而且每個Blob最高可儲存50G!
- 每個在Container裡面的Blob都有一個獨一無二的字串名稱。
- Blob也擁有Metadata可以設定,一樣是8k,也是一組<name,value>的形式。
最後,如上圖,因為是雲端儲存體,所以如果我們要取得Blob,可想而知,就一定是使用url來當作識別的位置啦!如下。
http://<account>.blob.core.windows.net/<container>/<blobname>;
來寫程式吧!
接下來,我們就來撰寫程式看看吧,我相信大家應該也沒有那麼多的錢可以租用Windows Azure服務,老實說,小弟我也沒那麼多錢,所以我的範例都是在本機上面做測試。
首先,要寫Windows Azure,必須先裝SDK,如果還沒裝的可以參考這篇。接下來就在Visual Studio裡面選擇Windows Azure專案。
到這邊,就可以選擇Web Role,但小弟我不喜歡在這邊選擇,會利用後續再增加的做法,有興趣的可以參考這篇,如果只想要簡單Demo,也可以直接在這邊選擇ASP.NET Web Role ( 中文翻角色 )。
接下來,我們先來看看最後寫完的範例程式執行起來的樣子吧;基本上就是長這樣,這隻程式的主要功能是可以上傳照片,並且可以刪除照片,如果有照片的話,可以從DropDownList可以看到檔名,並且於無資料那邊那個區塊,顯示所有雲端上的照片,這個範例也是Ruddy老師所說的,最經典且簡單的Blob範例,所以小弟我也造樣畫葫蘆,寫了此程式,並用此程式和大家解說。
首先我們先來看看HTML這邊的程式碼,基本就是很基本的ASP.NET。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="BlobTest.aspx.cs" Inherits="WebRole1.BlobTest" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:FileUpload ID="uploadImage" runat=server /> <asp:Button ID="uploadBtn" runat="server" Text="上傳" onclick="uploadBtn_Click" /> <asp:DropDownList ID="imageList" runat="server" AutoPostBack="true"></asp:DropDownList> <asp:Button ID="delBtn" runat="server" Text="刪除" onclick="delBtn_Click" /> <asp:ListView ID="imageView" runat="server"> <LayoutTemplate> <asp:PlaceHolder ID="itemPlaceHolder" runat="server" /> </LayoutTemplate> <EmptyDataTemplate> <h2>無資料</h2> </EmptyDataTemplate> <ItemTemplate> <!-- Eval:Eval是用於單向資料繫結,資料是唯讀的顯示。 Bind:Bind則是雙向的資料繫當,不但能讀取資料, 更具有Insert、Update、Delete功能,所以若您需要編輯更新、新增與刪除功能使用本方法。 語法如下。 --> <img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left" /> </ItemTemplate> </asp:ListView> </div> </form> </body> </html>
為了大家方便,我也做了一張對應圖給大家看。
接下來,我們開始撰寫這個BlobTest.aspx.cs吧( 此ASP.NET的程式名稱為BlobTest.aspx ),首先,既然要存取雲端的Blob,我們當然要先準備一下,存取的方法;首先,我們定義一個GetContainer的方法,來取得CloudBlobContainer,這樣後續就可以利用此方法來取得Container底下的Blob了;就如前面說的,任何的Container都是在Account之下,所以我們第一步,就是先取得Account,因為我們是在本機上模擬與測試,所以我們可以使用CloudStorageAccount這個靜態類別的DevelopmentStorageAccount這個靜態方法來取得開發用的Account;有了Account,就可以利用Account來取得Client來進行後續的處理,最後,我們就可以利用Client來取得Container ( 這個Container的名稱就是myContainer );到這邊還滿容易理解的,但是有人可能會有疑問,明明沒看到建立mycontainer這個Container的程式碼阿?沒錯,如果第一次執行的時候,到這邊的確還沒有在資料庫裡建立起mycontainer這個Container,我們只是利用client的GetContainerReference(“mycontainer”)這個方法來取得CloudBlobContainer這個物件,並且設定CloudBlobContainer裡面的Name屬性為mycontainer,後續我們才會於資料庫建立Container。
//取得Container private CloudBlobContainer GetContainer() { //取得Developer用的Storage Account。 CloudStorageAccount account = CloudStorageAccount.DevelopmentStorageAccount; //取得Storage的Client CloudBlobClient client = account.CreateCloudBlobClient(); //取得Container關聯。 return client.GetContainerReference("mycontainer"); }
有了取得Container這個方法,接下來,我們就來實際撰寫建立容器的方法,第一行滿簡單的,就是取回CloudBlobContainer這個容器,而第二行,我們就利用取得回來的CloudBlobContainer物件來建立一個Container,CreateIfNotExist(),這個方法很好玩,如果目前資料庫裡面已經有了名為mycontainer的這個容器,他就不會再建立了,如果沒有,就會在資料庫裏面建立一個。
//建立容器 private void CreateContainerExists() { CloudBlobContainer container = this.GetContainer(); //假如沒有這個Container,就建立一個。 container.CreateIfNotExist(); }
我們可以看到,如果程式執行到這邊,就會建立了一個mycontainer這個container。
( 這是一個很好用的Azure Storage 瀏覽工具 )
完成了存取後,我們來處理一下刷新Image這個方法,這個方法的用處是,當有上傳,或是刪除動作的時候,可以呼叫這個方法來重新刷新頁面上的控制項,詳細可以看程式碼的註解。
//刷新Image private void RefreshImage() { //ListBlobs可以取回此Container所有的Blobs,而後面可以傳入BlobRequestOptions這個物件, //這個物件可以設定,傳回來的條件。 //這邊條件設定的第一個是列出所有的Blob(UseFlatBlobListing = true) //並且抓取所有的Blob對象 ( BlobListingDetails = BlobListingDetails.All ) imageView.DataSource = this.GetContainer().ListBlobs(new BlobRequestOptions() { UseFlatBlobListing = true, BlobListingDetails = BlobListingDetails.All }); imageView.DataBind(); //每次刷新時,也清除DropDownList內的資訊。 imageList.Items.Clear(); var blobList = this.GetContainer().ListBlobs(); foreach (var item in blobList) { //每個Blob都有一個Uri屬性,這裡是取得Blob的檔案名稱。 imageList.Items.Add(item.Uri.Segments[3].ToString()); } //dropDownListItem其實是紀錄目前dropDownList選了哪一個item。 //這個變數是此類別的屬性,最後整體程式碼的時候會看到。 imageList.SelectedIndex = dropDownListItem; }
接下來,我們來撰寫存Image這個方法,這個方法會帶四個參數進來,第一個會傳入id,後續我們會利用Guid.NewGuid.ToString()來產生id,而第二個會傳入檔案名稱,第三個會傳入檔案的型態( image/jpeg ),第四個則是圖片binary的資料;程式碼的第一行,會使用GetBlobReference來取得Blob物件,這個方法和之前的GetContainerReference一樣,並不會馬上寫到資料庫,指示先將Blob物件準備好,後面的部分,就只是很簡單的設定檔案型態、定義MetaData,最後使用UploadByteArrary將資料寫到資料庫。
//存Image private void SaveImage(string id, string fileName, string contentType, byte[] data) { //利用GetBlobReference來取得Blob,第一個參數會帶進檔案名稱, //而此檔案名稱會化成Uri的一部分。 CloudBlob blob = this.GetContainer().GetBlobReference(fileName); //檔案型態 blob.Properties.ContentType = contentType; //定義MetaData NameValueCollection metadata = new NameValueCollection(); metadata["Id"] = id; metadata["Filename"] = fileName; blob.Metadata.Add(metadata); blob.UploadByteArray(data); }
最後我們要處理的就是刪除,刪除的原理很簡單,利用DropDownList所選取到的檔名去和娶回來的BlobList來比對,如果相同,就將此Blob移除。
//刪除 protected void delBtn_Click(object sender, EventArgs e) { if (this.GetContainer() == null) return; var blobList = this.GetContainer().ListBlobs(); foreach (var item in blobList) { string deleteImage = item.Uri.Segments[3].ToString(); //判斷DropDownList所選擇的item名稱和Blob名稱是否相同, //相同就刪除。 if (deleteImage == imageList.SelectedValue) { //利用GetBlobReference來取得Blob CloudBlob blob = this.GetContainer().GetBlobReference(item.Uri.ToString()); blob.DeleteIfExists(); //刪除完後刷新一下。 RefreshImage(); return; } } }
最後附上完整的程式碼。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Net; using System.Collections.Specialized; using System.Configuration; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.StorageClient; using Microsoft.WindowsAzure.ServiceRuntime; namespace WebRole1 { public partial class BlobTest : System.Web.UI.Page { int dropDownListItem = 0; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { //First this.CreateContainerExists(); dropDownListItem = imageList.SelectedIndex; this.RefreshImage(); } else { dropDownListItem = imageList.SelectedIndex; this.RefreshImage(); } } //上傳 protected void uploadBtn_Click(object sender, EventArgs e) { if (uploadImage.HasFile) { //SaveImage(string id, string fileName, string contentType, byte[] data) this.SaveImage(Guid.NewGuid().ToString(), uploadImage.FileName, uploadImage.PostedFile.ContentType, uploadImage.FileBytes); RefreshImage(); } } //取得Container private CloudBlobContainer GetContainer() { //取得Developer用的Storage Account。 CloudStorageAccount account = CloudStorageAccount.DevelopmentStorageAccount; //取得各種Storage的Client CloudBlobClient client = account.CreateCloudBlobClient(); //取得Container。 return client.GetContainerReference("mycontainer"); } //確保容器存在 private void CreateContainerExists() { CloudBlobContainer container = this.GetContainer(); //假如沒有這個Container,就建立一個。 container.CreateIfNotExist(); } //刷新Image private void RefreshImage() { //ListBlobs可以取回此Container所有的Blobs,而後面可以傳入BlobRequestOptions這個物件, //這個物件可以設定,傳回來的條件。 //這邊條件設定的第一個是列出所有的Blob(UseFlatBlobListing = true) //並且抓取所有的Blob對象 ( BlobListingDetails = BlobListingDetails.All ) imageView.DataSource = this.GetContainer().ListBlobs(new BlobRequestOptions() { UseFlatBlobListing = true, BlobListingDetails = BlobListingDetails.All }); imageView.DataBind(); //每次刷新時,也清除DropDownList內的資訊。 imageList.Items.Clear(); var blobList = this.GetContainer().ListBlobs(); foreach (var item in blobList) { //每個Blob都有一個Uri屬性,這裡是取得Blob的檔案名稱。 imageList.Items.Add(item.Uri.Segments[3].ToString()); } //dropDownListItem其實是紀錄目前dropDownList選了哪一個item。 //這個變數是此類別的屬性,最後整體程式碼的時候會看到。 imageList.SelectedIndex = dropDownListItem; } //存取Image private void SaveImage(string id, string fileName, string contentType, byte[] data) { //利用GetBlobReference來取得Blob,第一個參數會帶進檔案名稱, //而此檔案名稱會化成Uri的一部分。 CloudBlob blob = this.GetContainer().GetBlobReference(fileName); //檔案型態 blob.Properties.ContentType = contentType; //定義MetaData NameValueCollection metadata = new NameValueCollection(); metadata["Id"] = id; metadata["Filename"] = fileName; blob.Metadata.Add(metadata); blob.UploadByteArray(data); } //刪除 protected void delBtn_Click(object sender, EventArgs e) { if (this.GetContainer() == null) return; var blobList = this.GetContainer().ListBlobs(); foreach (var item in blobList) { string deleteImage = item.Uri.Segments[3].ToString(); //判斷DropDownList所選擇的item名稱和Blob名稱是否相同, //相同就刪除。 if (deleteImage == imageList.SelectedValue) { //利用GetBlobReference來取得Blob CloudBlob blob = this.GetContainer().GetBlobReference(item.Uri.ToString()); blob.DeleteIfExists(); //刪除完後刷新一下。 RefreshImage(); return; } } } } }
其實,這就是Blob的範例應用,雖然看起來程式碼很長,但其實還滿簡單的。