【IT168 技术】Windows Azure Storage 允许云计算应用的开发者在云端存储应用程序数据,为云计算提供耐用、可扩充、具备可用性且有效率的储存服务。Windows Azure Storage通过Development Storage和VS 2008/VS 2010结合使用,为开发者提供了熟悉、友好的程序设计界面。
Windows Azure Storage由三个重要部分构成:
Windows Azure Blob:存储大型数据,包括二进制数据和文件。
Windows Azure Table:存储表数据,类似关系数据库中的数据表,单有所不同,将在后文中介绍。
Windows Azure Queue:为异步工作提供分派消息服务,有点类似Windows系统的消息队列。
在某种程度上来说,我们可以把Blob认为是云端的文件系统。在本文中,我们的重点是Windows Azure Blob。通过对Blob架构分析,以一个完整的实例来一步一步介绍如何使用Blob进行开发。
Blob架构分析
对Blob架构的分析从两个方面来介绍:Blob应用程序架构和Blob存储结构。
(1) Blob应用程序架构
Blob应用程序架构如图1所示:

图1 Blob应用程序架构
通过图1我们可以看到,Blob存储数据是通过一个简单的层级关系来实现的,每个账户Account下有一个容器,这个容器就是用户定义的一套只有一个属性的Blobs(Blob集合),容器不直接存储数据,每个Blobs包含了多个Blob,这些Blob可以分为Blocks和Pages两类。Blob的存储是采用REST(Representational State Transfer,表象化状态转变)方式来进行的,有关REST可以访问维基百科地址:http://zh.wikipedia.org/zh-cn/REST来了解一下。
同时在图1中,我们可以看到存储账户的地址、Blob Container的地址、Blob的地址,如一个Blob的实际应用的地址如下:
http://127.0.0.1:10000/devstoreaccount1/imagefiles/0cd5c5f2-063e-4092-b960-a987c5654dee.png
那么标示我们使用的Storage是本地存储
账户:devstoreaccount1
Blob Container:imagefiles
Blob文件:0cd5c5f2-063e-4092-b960-a987c5654dee.png
(2) Blob存储结构
在本地开发的环境中,我们使用了安装在本地的SQL Express。要想了解Blob的存储结构,首先连接到数据库上看看。
使用SSMS连接本地的SQL Express数据库,如图1所示:

图2 连接本地SQL Express
在这里使用的SQL Express版本是SQL Express 2008,连接上本地SQL Express后,我们可以看到开发环境的存储数据库如图3所示

图3 开发环境存储表
由图3中的表我们可以看到,除了账户表(Account),这些表是针对Blob、Queue、Table三种存储服务来设计的,事实上,Blob的存储结构如图4所示:

图4 Blob的存储结构
应用与案例
在本文的案例中,我们将使用Blob存储上传到云端的图片文件,包括对云端的图片文件的列表展示、上传、删除、下载等功能。
在开始案例之前我们简要说明一下此案例的开发环境:
VS 2008 / VS 2010
SQL Express 2005 / SQL Express 2008
Windows Azure Tools for Microsoft Visual Studio 1.1,其下载的地址是:
http://www.microsoft.com/downloads/details.aspx?familyid=5664019E-6860-4C33-9843-4EB40B297AB6&displaylang=en
Windows Azure SDK
http://www.microsoft.com/downloads/details.aspx?FamilyID=21910585-8693-4185-826e-e658535940aa&displaylang=en
在本例中,将使用VS 2010来创建项目,下面我们开始介绍这个案例的详细开发步骤:
第一步:创建项目
在开始|所有程序里打开Microsoft Visual Studio 2010,创建新的Cloud项目DemoStorageBlob, 如图5所示:
图5 创建项目DemoStoragBlob
输入项目名称DemoStoragBlob,解决方案名称后,点击“确定 OK”,自动打开添加云应用项目界面,如图6所示:

图6 创建Web Role
选择Asp.Net Web Role,创建名为BlobWebRole的项目,点击“确定 OK”,我们创建的项目结构最终如图7所示:

图7 DemoStorageBlob项目结构
第二步:添加WebRole程序代码
在WebRole程序代码设计部分,我们要完成的任务是:设计图片实体类、设计上传显示界面,完成上传、显示、删除功能,可以说是整个案例的核心部分。
本案例中我们要处理的Blob对象是图片文件,因此,在BlobWebRole项目中,添加一个Image图片的实体类ImageEntity,代码如下:
{
public Uri FileUri { get; set; }
public string FileName { get; set; }
public long Length { get; set; }
public string FolderName { get; set; }
public bool IsReadOnly { get; set; }
public bool IsExists { get; set; }
public string FullFileName { get; set; }
public string ExtendName { get; set; }
public string CreatorName { get; set; }
public DateTime CreateTime { get; set; }
}
添加完ImageEntity类的代码后,对Default.Aspx页面进行简单的设计,在Default页面里,有两部分,一部分是上传图片,另一部分是列表显示Blob里的数据,代码如下:
<div>
<div><!--上传图片-->
<asp:Label ID="lblFilePath" Text="上传文件" AssociatedControlID="fileUploadControl"
runat="server" />
<asp:FileUpload ID="fileUploadControl" runat="server" />
<asp:RequiredFieldValidator ID="filUploadValidator" ControlToValidate="fileUploadControl"
ValidationGroup="fileInfoGroup" ErrorMessage="请选择文件!" runat="Server">
</asp:RequiredFieldValidator>
<br />
<asp:Label ID="lblFileName" Text="文件名称:" AssociatedControlID="txtFileName" runat="server" />
<asp:TextBox ID="txtFileName" runat="server" />
<asp:RequiredFieldValidator ID="fileNameValidator" ControlToValidate="txtFileName"
ValidationGroup="fileInfoGroup" ErrorMessage="请输入文件名!" runat="Server">
</asp:RequiredFieldValidator>
<br />
<asp:Label ID="lblCreator" Text="创 建 者:" AssociatedControlID="txtCreator" runat="server" />
<asp:TextBox ID="txtCreator" runat="server" />
<asp:RequiredFieldValidator ID="submitterValidator" ControlToValidate="txtCreator"
ValidationGroup="fileInfoGroup" ErrorMessage="请输入创建者!" runat="Server">
</asp:RequiredFieldValidator>
<br />
<asp:Button ID="btnSave" Text="保存" CausesValidation="true" ValidationGroup="fileInfoGroup"
runat="server" OnClick="btnSave_Click" />
<br />
<br />
<asp:Label ID="statusMessage" runat="server" />
</div>
<div><!--列表显示图片-->
<asp:GridView ID="fileView" AutoGenerateColumns="false" DataKeyNames="FileUri" runat="server"
OnRowCommand="RowCommandHandler">
<Columns>
<asp:ButtonField Text="Delete" CommandName="DeleteItem" />
<asp:HyperLinkField HeaderText="Link" DataTextField="FileName" DataNavigateUrlFields="FileUri" />
<asp:BoundField DataField="CreatorName" HeaderText="Created by" />
<asp:BoundField DataField="FolderName" HeaderText="FolderName" />
<asp:BoundField DataField="IsReadOnly" HeaderText="IsReadOnly" />
<asp:BoundField DataField="IsExists" HeaderText="IsExists" />
<asp:BoundField DataField="FullFileName" HeaderText="FullFileName" />
<asp:BoundField DataField="ExtendName" HeaderText="ExtendName" />
<asp:BoundField DataField="CreateTime" HeaderText="CreateTime" />
</Columns>
</asp:GridView>
</div>
</div>
</form>
下面就是本案例中最重要的核心代码了。
打开defau.aspx.cs文件,添加如下代码:
添加引用:
using Microsoft.WindowsAzure.StorageClient;
添加代码:
private CloudBlobContainer _BlobContainer = null;
private const string conContainerAddress = "imagefiles";
private const string conConfigStorageSetting = "BlobConnectionString";
protected void Page_Load(object sender, EventArgs e)
{
//设置Windows Azure Storage连接
var storageAccount = CloudStorageAccount.FromConfigurationSetting(conConfigStorageSetting);
_BlobClient = storageAccount.CreateCloudBlobClient();
//获取或创建container
_BlobContainer = _BlobClient.GetContainerReference(conContainerAddress);
_BlobContainer.CreateIfNotExist();
//设置权限
var permissions = new BlobContainerPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
_BlobContainer.SetPermissions(permissions);
//显示当前列表
DisplayFileList();
在这里我们定义了常量conContainerAddress,它表示我们将要创建的Blob Container名称,注意在这里设置的时候一定要全部使用小写字母,如果使用匈牙利命名法,可能会出现错误。
在这里初始化了Blob信息,主要是设置了Windows Azure Storage连接的账户、Blob Container的名称。
/// 列表
/// </summary>
private void DisplayFileList()
{
var blobs = _BlobContainer.ListBlobs();
var filesList = new List<ImageEntity>();
//查询
foreach (var blobItem in blobs)
{
var cloudBlob = _BlobContainer.GetBlobReference(blobItem.Uri.ToString());
cloudBlob.FetchAttributes();
//实体字段
filesList.Add(new ImageEntity()
{
FileUri = blobItem.Uri,
FileName = cloudBlob.Metadata["FileName"],
Length = long.Parse(cloudBlob.Metadata["Length"]),
FolderName = cloudBlob.Metadata["FileName"],
IsReadOnly = bool.Parse(cloudBlob.Metadata["IsReadOnly"]),
IsExists = bool.Parse(cloudBlob.Metadata["IsExists"]),
FullFileName = cloudBlob.Metadata["FullFileName"],
ExtendName = cloudBlob.Metadata["ExtendName"],
CreatorName = cloudBlob.Metadata["CreatorName"],
CreateTime = DateTime.Parse(cloudBlob.Metadata["CreateTime"])
});
}
//绑定列表
fileView.DataSource = filesList;
fileView.DataBind();
}
在这里使用cloudBlob.FetchAttributes()来获取Blob的属性或者元数据,把查询出来的ImageEntity实体结合绑定到GridView上去,这样我们在列表里就能看到有哪些Blob的数据。
/// 上传
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnSave_Click(object sender, EventArgs e)
{
//获取扩展名
string extension = System.IO.Path.GetExtension(fileUploadControl.FileName);
//创建Blob并且重命名
var blob = _BlobContainer.GetBlobReference(Guid.NewGuid().ToString() + extension);
blob.UploadFromStream(fileUploadControl.FileContent);
//设置元数据到Blob
blob.Metadata["FileName"] = txtFileName.Text;
blob.Metadata["Length"] = fileUploadControl.FileContent.Length.ToString();
DirectoryInfo di = new DirectoryInfo(string.Format("{0}\\", conContainerAddress));
blob.Metadata["FolderName"] = di.Name;
FileInfo file = new FileInfo(fileUploadControl.PostedFile.FileName);
blob.Metadata["IsReadOnly"] = file.IsReadOnly.ToString();
blob.Metadata["IsExists"] = file.Exists.ToString();
blob.Metadata["FullFileName"] = file.FullName;
blob.Metadata["ExtendName"] = file.Extension;//extension
blob.Metadata["CreatorName"] = txtCreator.Text;
blob.Metadata["CreateTime"] = DateTime.Now.ToShortTimeString();
blob.SetMetadata();
//设置属性
blob.Properties.ContentType = fileUploadControl.PostedFile.ContentType;
blob.SetProperties();
//显示列表
DisplayFileList();
txtFileName.Text = string.Empty;
txtCreator.Text = string.Empty;
statusMessage.Text = string.Empty;
}
在上传方法中,使用了blob.SetMetadata()来设置Blob的元数据,把元数据存到了Blob中,blob.SetProperties()来设置Blob的属性,并“Set”进去。
{
//删除
if (e.CommandName == "DeleteItem")
{
// 获取索引
var index = Convert.ToInt32(e.CommandArgument);
var blobUri = fileView.DataKeys[index].Value.ToString();
//Get the container
var blob = _BlobContainer.GetBlobReference(blobUri);
blob.DeleteIfExists();
}
//更新列表
DisplayFileList();
}
在点击“删除 Delete”的时候,将删除选中的Blob,调用了blob.DeleteIfExists(),注意在使用Blob对象时,首先要使用Container的GetBlobReference方法来获取到Blob对象。
第三步:配置应用程序
配置应用程序有两种方法,一种是直接修改服务定义文件:ServiceDefinition.csdef,服务配置文件ServiceConfiguration.cscfg;另一种是使用UI来直接配置。不管使用哪种方法,其最终的结果是一样的。在这里我们来使用第二种方法,添加配置节BlobConnectionString。
打开DemoStorageBlob下的Roles文件夹,双击BlobWebRole,打开“设置 Settings”页面,添加BlobConnectionString,如图9所示:

图9 添加BlobConnectionString
添加完后,我们的服务定义文件自动发生了修改:
<Setting name="BlobConnectionString" />
</ConfigurationSettings>
服务配置文件也同样发生了修改
<ConfigurationSettings>
<Setting name="BlobConnectionString" value="UseDevelopmentStorage=true" />
</ConfigurationSettings>
服务配置文件也同样发生了修改
第四步:设置运行调试环境
配置完成后,编译应用程序,保证没有编译错误的情况,我们要进行调试运行程序之前的设置工作,设置工作主要是Development Fabric和Storage,设置项目的启动项和起始页。
首先需要设置DemoStorageBlob为启动项目,如图10所示:

图10 设置启动项目
设置完启动项目后,设置BlobWebRole下的Default为起始页。设置完成后,在开始|所有程序| Windows Azure SDK v1.2|打开Development Fabric,确保我们调试时候的模拟器是开着的,在本案例中,要确保Storage的Blob是运行的,界面如图11所示:

图11 启动development Storage
第五步:调试运行程序
这里没有设置断点来调试程序,直接“Run”起来我们的案例,初始化的界面如图12所示:

图12 上传初始界面
选择文件,输入文件名称、创建者,点击上传,上传几个文件后的界面如图13所示:

图13 上传4个图片文件到Blob
图13中已经上传了4个图片文件,点击文件文件名,可以在打开已经上传的图片文件,点击“Delete”,删除文件TestBlob4,界面如图14所示:

图14 删除后的界面
不仅能通过运行的前台页面来查看文件,还可以在VS 2010中通过Server Explorer来查看Blob对象,这也是VS开发平台强大的地方之一,通过VS打开Blob的界面如图15所示:

图15 服务器浏览器查看Blob
在图15所示的界面中,可以输入文件名进行查找,可以打开文件,另存文件到本地。
我们在页面上做的所有操作的结果都存入到了SQL Express的数据库中,在Blob架构分析中,已经介绍了如何连接SQL Express数据库,连接上数据库后,我们查看一下与Blob相关的几个表里数据,界面如图16所示:

图16 查询Blob数据
总结
本文介绍了Windows Azure Storage三驾马车之一的Blob,对开发者来说Blob是一个存储大容量二进制文件或文件的“虚拟空间”,同时从应用程序和数据结构两个方面介绍了Blob的结构,并通过图片管理的一个应用案例的讲解,使大家对Blob有一个深入的了解。