Azure Blob 和实体表集成,扩展缩略图示例






4.54/5 (5投票s)
本文描述了在 Windows Azure 表上执行 CRUD(创建、读取、更新、删除)操作的概念,以及表数据如何与 blob 进行交互。
引言
Windows Azure 是 Microsoft 推出的一个令人兴奋的平台,它支持海量可扩展的云端表,可以包含数十亿个实体和 TB 级的数据。Windows Azure 存储允许用户将数据存储在 Windows Azure Blob(提供大项目存储)和 Windows Azure Table(提供结构化存储)中。清晰理解如何与实体表和 blob 交互,将使我们能够充分欣赏和利用 Azure 的所有功能。本文描述了在 Windows Azure 表上执行 CRUD(创建、读取、更新、删除)操作的概念,以及表数据如何与 Blob 进行交互。
背景
Azure SDK 附带了一些有助于理解该技术的示例。Azure SDK 已发布两个主要版本:第一个版本在 2009 年 3 月发布,第二个版本在 2009 年 11 月发布。3 月份的版本包含用于在本地开发存储以及云中处理 Blob 和表存储的 StorageClient
类。在 11 月份的版本中,Microsoft 发布了存储服务 API,并简化了诸如存储字符串操作等内容。
SDK 附带了一些示例。其中两个示例,“缩略图”和“个人网站”,提供了 Azure 开发环境的绝佳体验。“缩略图”示例有助于理解 Azure 中的 Blob 操作和工作流角色。“个人网站”示例包含一个如何处理表和 Blob 的示例。“缩略图”示例已针对新的存储客户端 API 进行了修改,而“个人网站”尚未修改。
本文使用“缩略图”示例并对其进行了扩展。“缩略图”示例允许您浏览图像文件并将其保存在 Blob 存储中。创建一个工作流角色,该角色会创建您上传图像的缩略图,并将图像及其缩略图存储为 blob。网页显示 Blob 存储中的缩略图,并每十秒刷新一次页面。
在此扩展中,添加了以下功能:
- 输入要下载的图片的名称、日期和注释。
- 修改存储在表中的字段。
- 删除图片及其关联数据。
- 列表中点击缩略图会显示原始图片。
- 取消每秒自动刷新,并使用
UpdatedPanel
更新页面相关部分,避免不必要的服务器往返。
输出
原始示例的默认页面如下所示:
本文描述的扩展代码生成的默认页面如下所示:
设置
让我们深入代码。您可以解压本文的工程代码,并在 Visual Studio 中打开解决方案来跟随本讨论。
我假设您知道如何设置 Azure,已下载 SDK,并且能够在 Visual Studio 2008 环境中运行 SDK 中的示例。如果您不知道如何进行设置,可以参考 Microsoft 的帮助视频和文章。您可能还想参考原始的缩略图示例来跟进此代码中的一些添加。
11 月份 SDK 的一个新功能是使用 DataConnectionString
。您可以在 roles 文件夹中找到 WebRole 和 WorkerRole 项目,右键单击它们,然后查看属性。这将显示设置 DataConnectionString
的菜单。因此,不再需要像 3 月份版本那样处理 ServiceConfiguration 和 ServiceDefinition XML 文件。
类
Code 文件夹中的 PhotoTableDataModel
、PhotoTableDataServiceContext
和 PhotoTableDataSource
类是新的,并且与 PhotoTable 扩展相关。这些类的想法来源于 Jim Nakashima 在其博客“演练:Windows Azure 表存储(2009 年 11 月及以后)”中提供的方向。
原始示例 Default.aspx.cs 文件中与 Blob 和 Queue 相关的函数已被移除,并添加到了 Blob 类中,以便与 Table 类保持一致。
这些类还会在表和 Blob 容器不存在时创建它们。
存储 blob 和关联的表行
当您运行程序时,您会看到默认页面。您可以浏览要上传的图像,输入名称、日期和任何注释,然后按“提交”按钮。会引发“提交”按钮的 Click
事件。代码如下:
protected void submitButton_Click(object sender, EventArgs e)
{
if (upload.HasFile)
{
DateTime DTN = DateTime.Now;
long DTNTicks = DateTime.Now.Ticks;
var name = string.Format("{0:10}_{1}", DTNTicks, Guid.NewGuid());
blobclass1.GetPhotoGalleryContainer().GetBlockBlobReference(
name).UploadFromStream(upload.FileContent);
blobclass1.GetThumbnailMakerQueue().AddMessage(
new CloudQueueMessage(System.Text.Encoding.UTF8.GetBytes(name)));
System.Diagnostics.Trace.WriteLine(String.Format("Enqueued '{0}'", name));
AddPhotoTableRow(name,DTN);
}
//Delay to Make sure the picture has been stored
//in the blob before the Photos are updated
System.Threading.Thread.Sleep(1000);
Display_Thumbnails();
}
此代码扩展了原始示例代码。通过组合 Now
时间和 Guid
来创建一个唯一的 ID(名称)。使用此 ID(名称)创建一个容器,并创建一个工作队列。工作队列会创建缩略图图像并将其存储在缩略图中。AddPhotoTablRow
将提交表单中的数据添加到 Azure 表存储。延迟一秒后,将调用 Display_Thumbnails
。稍后将介绍 Display_Thumbnails
、AddPhotoTableRow
和 Delay
。
显示缩略图
对原始示例的一个主要增强是在 ListView
中显示表数据和关联图像。ASPX 和代码隐藏如下所示。原始示例代码仅显示缩略图。
<asp:ListView ID="thumbnails" runat="server" >
<LayoutTemplate>
<asp:Image ID="itemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<tr style = "border:5px" >
<td>
<%# Eval("PhotoName") %><br />
<%# DataBinder.Eval(Container.DataItem, "PhotoTakenDate", "{0:MM.dd.yyyy}")%>
<br />
<%# Eval("PhotoComment") %>
</td>
<td>
<asp:ImageButton ID="photoimagebutton" runat="server"
ImageUrl='<%# Eval("Url") %>'
OnCommand="Select_Image_Command" CommandName="Select_Image"
CommandArgument='<%# Eval("rowKey") %>' />
</td>
<td>
<asp:Button runat="server" Text="Delete Data" ID="Delete_Data"
OnCommand="Delete_Data_Command" CommandName="Delete_Data"
CommandArgument='<%# Eval("rowKey") %>' />
</td>
<td>
<asp:Button runat="server" Text="Update Data" ID="Update_Data"
OnCommand="Update_Data_Command" CommandName="Update_Data"
CommandArgument='<%# Eval("rowKey") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
代码隐藏部分
private void Display_Thumbnails()
{
var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var context = new PhotoTableDataServiceContext(
account.TableEndpoint.ToString(), account.Credentials);
List<PhotoTableDataModelwithUrl> PTDMUrl = new List<PhotoTableDataModelwithUrl>();
var bloblist = from o in
blobclass1.GetPhotoGalleryContainer().GetDirectoryReference("thumbnails").ListBlobs()
select new { Url = o.Uri };
foreach (PhotoTableDataModel item in context.PhotoTableData)
{
PhotoTableDataModelwithUrl PTDMUrlItem = new PhotoTableDataModelwithUrl();
PTDMUrlItem.PartitionKey = item.PartitionKey ;
PTDMUrlItem.RowKey = item.RowKey;
PTDMUrlItem.PhotoName = item.PhotoName;
PTDMUrlItem.PhotoTakenDate = item.PhotoTakenDate;
PTDMUrlItem.PhotoUploadDate = item.PhotoUploadDate;
PTDMUrlItem.PhotoComment = item.PhotoComment;
foreach (var s in bloblist)
{
if (s.Url.ToString().Contains(PTDMUrlItem.RowKey))
{
PTDMUrlItem.Url = s.Url.ToString();
}
}
PTDMUrl.Add(PTDMUrlItem);
}
thumbnails.DataSource = PTDMUrl;
thumbnails.DataBind();
up1.Update();
}
使用表数据和关联的缩略图创建一个列表。使用 LINQ 查询(类似于原始示例中的查询),创建一个所有 blob 的 URL 列表。为 DataTable
中的所有行创建第二个列表。使用第一个列表,将相关 URL 添加到第二个列表,然后将该列表绑定到 ListView
,从而生成显示。
UpdatePanel
中,用户可以对其进行更改。CommandArgument
用于存储行键,该键用于标识行及其关联的 blob。
AddPhotoTableRow 用于存储与 blob 关联的表行
AddPhotoTableRow
代码如下:
private void AddPhotoTableRow(string blobname, DateTime DTN)
{
PhotoTableDataModel PTDM = new PhotoTableDataModel();
PTDM.PartitionKey = "Album1";
PTDM.RowKey = blobname;
if (TextBoxName.Text!="")
{
PTDM.PhotoName = TextBoxName.Text;
}
else
{
PTDM.PhotoName = "Name";
}
if (TextBoxComment.Text != "")
{
PTDM.PhotoComment = TextBoxComment.Text;
}
else
{
PTDM.PhotoComment = "Comment";
}
if (TextBoxDate.Text != "")
{
PTDM.PhotoTakenDate = Convert.ToDateTime(TextBoxDate.Text);
}
else
{
PTDM.PhotoTakenDate = DTN;
}
PTDM.PhotoUploadDate = DTN;
PTDS.Insert(PTDM);
}
使用唯一的 blob 名称作为行键创建一个行。分区键是 Album1
。在将来的版本中,可以使用它来存储相册名称以进一步扩展此代码。PhotoTable 类使添加照片表行变得轻而易举。只需将数据与相关的分区键和行键填充到一行中,然后调用 Insert
(行)方法即可!
删除表行和 blob
删除数据的代码如下:
protected void Delete_Data_Command(object sender,
System.Web.UI.WebControls.CommandEventArgs e)
{
var blobname = e.CommandArgument.ToString();
var blobcontainer = blobclass1.GetPhotoGalleryContainer();
var blob = blobcontainer.GetBlobReference(blobname);
blob.DeleteIfExists();
var blobt = blobcontainer.GetBlobReference("thumbnails/" + blobname);
blobt.DeleteIfExists();
PhotoTableDataModel PTDM = (from PTDM1 in PTDS.Select()
where PTDM1.RowKey == e.CommandArgument.ToString()
select PTDM1).FirstOrDefault();
PTDS.Delete(PTDM);
Display_Thumbnails();
}
BlobClass
和 Photo Table 类可以轻松删除记录。CommandArgument
提供用于删除 blob 和缩略图的引用。使用 LINQ 命令获取行引用,然后删除。
更新表行
更新数据行的代码如下:
protected void Update_Data_Command(object sender,
System.Web.UI.WebControls.CommandEventArgs e)
{
PhotoTableDataModel PTDM = (from PTDM1 in PTDS.Select()
where PTDM1.RowKey == e.CommandArgument.ToString()
select PTDM1).FirstOrDefault();
TextBoxNameEdit.Text = PTDM.PhotoName;
TextBoxDateEdit.Text = PTDM.PhotoTakenDate.ToShortDateString();
TextBoxCommentEdit.Text = PTDM.PhotoComment;
TextBoxRowKeyEdit.Text = PTDM.RowKey;
UpdateEditPanel1.Update();
}
CommandArgument
提供行引用;使用 LINQ 命令获取行。从输入的文本中获取行信息,然后通过调用 Update
方法来更新行。SubmitChanges
与 UpdateTableRow
非常相似,并使用用户修改的数据。
更新面板
原始示例进行数据更新的一个有趣方式是使用一个每秒更新一次页面的计时器。不清楚为什么这样做,因为即使没有用户交互,每秒也会产生一次服务器往返。此修订版代码包含 UpdatePanel
,当由于用户交互导致信息更改时,相关代码会更新它们。代码摘录如下:
<asp:UpdatePanel ID="up1" runat="server" UpdateMode="Conditional">
up1.Update();
此技术允许在必要时进行更新。当上传新照片时,队列有时需要几百毫秒才能创建缩略图。在 ListView
中更新缩略图之前会延迟 1 秒,以确保显示刚刚上传的缩略图。
关注点
此项目的激动人心之处在于简化了 Azure 表上的 CRUD 操作以及将 blob 与表关联。由于新的 API,代码变得非常用户友好且易于理解。AJAX UpdatePanel
还减少了与服务器的交互。
历史
First version.