В качестве примера рассмотрим работу простого веб - приложения для загрузки изображений в хранилище Windows Azure Blob:
Создайте проект облачной службы и добавьте веб - роль в решение.
В свойствах веб - роли определим строку подключения к эмулятору хранилища Azure.
(рис 20.1) Также нам понадобится строковый параметр ContainerName, определяющий имя Blob - контейнера для изображений.
(рис 20.2) Создадим веб - форму AzureBlobSamle.aspx и добавим ссылки в C# класс данной страницы
using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.StorageClient; using System.Configuration;
Далее, поскольку Blob - контейнер должен быть доступным для анонимных пользователей необходимо создать метод, подтверждающий наличие контейнера:
private void EnsureContainerExists()
{
var container = GetContainer();
container.CreateIfNotExist();
var permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
}
Как видно, в данном методе присутствует ссылка на несуществующий пока GetConteiner() , который должен возвращать Blob-контейнер для осуществления операций с Blob-объектами.
private CloudBlobContainer GetContainer()
{
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSettingPublisher) =>
{
var connectionString =
RoleEnvironment.GetConfigurationSettingValue(configName);
configSettingPublisher(connectionString);
}
);
var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var client = account.CreateCloudBlobClient();
return client.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("ContainerName"));
}
Теперь необходимо заняться самой формой. На форме буду присутствовать текстовые поля, задающие параметры изображения и диалог загрузки файла.
(рис 20.3) asp - код веб формы:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AzureBlobSample.aspx.cs"
Inherits="WebRole1.AzureBlobSample" %>
<!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 runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Font-Bold="True" Font-Size="X-Large"
Text="My Image Gallery - Azure Blob Example"></asp:Label>
<br />
<br />
<table style="width:100%;">
<tr>
<td>
<asp:Label ID="lb_name" runat="server" Text="Название"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_label" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_desc" runat="server" Text="Описание"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_desc" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_file" runat="server" Text="Файл"></asp:Label>
</td>
<td>
<asp:FileUpload ID="fu_upload" runat="server" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Button ID="btn_upload" runat="server" onclick="btn_upload_Click"
Text="Загрузить" />
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_status" runat="server"></asp:Label>
</td>
<td>
<asp:ListView ID="lv_images" runat="server">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</LayoutTemplate>
<EmptyDataTemplate>
<h2>No Data Available</h2>
</EmptyDataTemplate>
<ItemTemplate>
<div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
</li>
</ul>
<img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
</div>
</ItemTemplate>
</asp:ListView>
</td>
</tr>
<tr>
<td>
<br />
</td>
<td>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Отметим, что элемент управления, при помощи которого мы будем отображать имеющиеся изображения - это ListView с идентификатором lv_images. Для того чтобы сделать возможным повторени вышеуказанного шаблона для каждого элемента списка мы использовали Repeater (подробнее см. в списке вспомогательных материалов)
Теперь в метод Page_Load добавим код, который при первой загрузке нашей страницы будет проверять наличие контейнера и выводить сообщение о возможных ошибках и привязывать lv_images к источнику данных, вызовом метода RefreshGallery:
try
{
if (!IsPostBack)
{
this.EnsureContainerExists();
}
this.RefreshGallery();
}
catch (System.Net.WebException we)
{
lb_status.Text = "Network error: " + we.Message;
if (we.Status == System.Net.WebExceptionStatus.ConnectFailure)
{
lb_status.Text += "<br />Please check if the blob service is running at " +
ConfigurationManager.AppSettings["storageEndpoint"];
}
}
catch (StorageException se)
{
Console.WriteLine("Storage service error: " + se.Message);
}
Метод RefreshGallery - привязывает lv_images к источнику данных(контейнеру бинарных объектов):
private void RefreshGallery()
{
lv_images.DataSource =
this.GetContainer().ListBlobs(new BlobRequestOptions()
{
UseFlatBlobListing = true,
BlobListingDetails = BlobListingDetails.All
});
lv_images.DataBind();
}
Запустим приложение и убедимся, что оно выполняется верно.
(рис 20.4)
Необходимо добавить следующую ссылку в AzureBlobSample.cs:
using System.Collections.Specialized;
Создадим метод, сохраняющий бинарный объект и задающий его метаданные:
private void SaveImage(string id, string name, string description, string tags,
string fileName, string contentType, byte[] data)
{
// Создание BLob - объекта в контейнере
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
// определение метаданных, добавление метаданных blob-объекту и загрузка данных
try
{
blob.UploadFromStream(fu_upload.FileContent);
blob.Metadata["Id"] = id;
blob.Metadata["Filename"] = fileName;
blob.Metadata["ImageName"] = String.IsNullOrEmpty(name) ? "unknown" : name;
blob.Metadata["Description"] = String.IsNullOrEmpty(description) ? "unknown" : description;
blob.SetMetadata();
lv_images.DataBind();
}
catch (Exception ex)
{ lb_status.Text = ex.Message; }
}
}
Остается только написать метод - обрабатывающий событие нажатия кнопки btn_upload:
protected void btn_upload_Click(object sender, EventArgs e)
{
if (fu_upload.HasFile)
{
lb_status.Text = "Inserted [" + fu_upload.FileName + "] -
Content Type [" + fu_upload.PostedFile.ContentType + "] -
Length [" + fu_upload.PostedFile.ContentLength + "]";
this.SaveImage(Guid.NewGuid().ToString(),
tb_name.Text,
tb_desc.Text,
fu_upload.FileName,
fu_upload.PostedFile.ContentType,
fu_upload.FileBytes
);
RefreshGallery();
}
else
lb_status.Text = "No image file";
}
Теперь можно проверить функцию загрузки изображения, выбрав его через диалог загрузки файла.
(рис 20.5) Надо сказать, что в таком виде информация об изображении малоинформативная. Blob, как мы знаем, может хранить привязанные к нему метаданные. Добавим в наше решение функционал, позволяющий получать и отображать метаданные, ассоциированные с конкретным изображением.
Для начала, необходимо создать метод обрабатывающий событие ItemDataBound элемента управления lv_images:
protected void lv_images_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var metadataRepeater = e.Item.FindControl("blobMetadata") as Repeater;
var blob = ((ListViewDataItem)(e.Item)).DataItem as CloudBlob;
if (blob != null)
{
if (blob.SnapshotTime.HasValue)
{
var delBtn = e.Item.FindControl("deleteBlob") as LinkButton;
if (delBtn != null) delBtn.Text = "Delete Snapshot";
var snapshotBtn = e.Item.FindControl("SnapshotBlob") as LinkButton;
if (snapshotBtn != null) snapshotBtn.Visible = false;
}
if (metadataRepeater != null)
{
metadataRepeater.DataSource = from key in blob.Metadata.AllKeys
select new
{
Name = key,
Value = blob.Metadata[key]
};
metadataRepeater.DataBind();
}
}
}
}
Теперь запустим наше приложение и получим следующее:
(рис 20.6) Слева от изображения появился список метаданных.
Для начала необходимо добавить следующий asp - код для lv_images:
...
div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
<asp:LinkButton ID="deleteBlob"
OnClientClick="return confirm('Delete image?');"
CommandName="Delete"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Удалить" oncommand="OnDeleteImage" />
...
Это необходимо для формировании кнопки "Delete" в рамках нашего ListView. Кроме того, этот кол содержит ссылку на метод, выполняющийся при нажатии кнопки.
Метод OnDeleteImage:
protected void OnDeleteImage(object sender, CommandEventArgs e)
{
try
{
if (e.CommandName == "Delete")
{
var blobUri = (string)e.CommandArgument;
var blob = this.GetContainer().GetBlobReference(blobUri);
blob.DeleteIfExists();
}
}
catch (StorageClientException se)
{
lb_status.Text = "Storage client error: " + se.Message;
}
catch (Exception) { }
RefreshGallery();
}
Данный метод определяет бинарный объект и удаляет его.
Запустите приложение и протестируйте функцию удаления.
(рис 20.7)
Копирование бинарных объектов также поддерживается Windows Azure. В рамках данного задания мы рассмотрим этот механизм.
Для формирования кнопки "Копировать" в списке имеющихся изображений, добавим следующий asp - код для lv_images :
...
<div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
...
<asp:LinkButton ID="CopyBlob"
OnClientClick="return confirm('Copy image?');"
CommandName="Copy"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Copy" oncommand="OnCopyImage" />
...
Как и в предыдущем задании, создадим метод OnCopyImage:
protected void OnCopyImage(object sender, CommandEventArgs e)
{
if (e.CommandName == "Copy")
{
// Prepare an Id for the copied blob
var newId = Guid.NewGuid();
// получение исходного объекта
var blobUri = (string)e.CommandArgument;
var srcBlob = this.GetContainer().GetBlobReference(blobUri);
// создание нового бинарного объекта
var newBlob = this.GetContainer().GetBlobReference(newId.ToString());
// копирование содержимого исходного объекта
newBlob.CopyFromBlob(srcBlob);
// получаем метаданные для нового объекта
newBlob.FetchAttributes(new BlobRequestOptions { BlobListingDetails = BlobListingDetails.Metadata });
// изменение метаданных нового объекта, чтобы показать, что это копия
newBlob.Metadata["ImageName"] = "Copy of \"" + newBlob.Metadata["ImageName"]
+ "\"";
newBlob.Metadata["Id"] = newId.ToString();
newBlob.SetMetadata();
RefreshGallery();
}
}
Проверим функционал:
(рис 20.8) В случае, если выполнение задания вызвало сложности и затруднения, в приложениях к данной практической работе вы найдете итоговый программный код в том виде, в котором он необходим для последнего задания.
Работа с Windows Azure Blob
Repeater
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="AzureBlobSample.aspx.cs" Inherits="WebRole1.AzureBlobSample" %>
<!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 runat="server">
<title></title>
<style type="text/css">
.style1
{
height: 25px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Font-Bold="True" Font-Size="X-Large"
Text="My Image Gallery - Azure Blob Example"></asp:Label>
<br />
<br />
<table style="width:100%;">
<tr>
<td>
<asp:Label ID="lb_name" runat="server" Text="Название"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_name" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_desc" runat="server" Text="Описание"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_desc" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td class="style1">
<asp:Label ID="lb_file" runat="server" Text="Файл"></asp:Label>
</td>
<td class="style1">
<asp:FileUpload ID="fu_upload" runat="server" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Button ID="btn_upload" runat="server" onclick="btn_upload_Click"
Text="Загрузить" />
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_status" runat="server"></asp:Label>
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:ListView ID="lv_images" runat="server"
onitemdatabound="lv_images_ItemDataBound">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</LayoutTemplate>
<EmptyDataTemplate>
<h2>No Data Available</h2>
</EmptyDataTemplate>
<ItemTemplate>
<div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
<asp:LinkButton ID="deleteBlob"
OnClientClick="return confirm('Delete image?');"
CommandName="Delete"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Удалить" oncommand="OnDeleteImage" />
<asp:LinkButton ID="CopyBlob"
OnClientClick="return confirm('Copy image?');"
CommandName="Copy"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Копировать" oncommand="OnCopyImage" />
</li>
</ul>
<img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
</div>
</ItemTemplate>
</asp:ListView>
</td>
</tr>
<tr>
<td>
<br />
</td>
<td>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Configuration;
using System.Collections.Specialized;
namespace WebRole1
{
public partial class AzureBlobSample : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (!IsPostBack)
{
this.EnsureContainerExists();
}
this.RefreshGallery();
}
catch (System.Net.WebException we)
{
lb_status.Text = "Network error: " + we.Message;
if (we.Status == System.Net.WebExceptionStatus.ConnectFailure)
{
lb_status.Text += "<br />Please check if the blob service is running at " +
ConfigurationManager.AppSettings["storageEndpoint"];
}
}
catch (StorageException se)
{
Console.WriteLine("Storage service error: " + se.Message);
}
}
private void RefreshGallery()
{
lv_images.DataSource =
this.GetContainer().ListBlobs(new BlobRequestOptions()
{
UseFlatBlobListing = true,
BlobListingDetails = BlobListingDetails.All
});
lv_images.DataBind();
}
private CloudBlobContainer GetContainer()
{
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSettingPublisher) =>
{
var connectionString =
RoleEnvironment.GetConfigurationSettingValue(configName);
configSettingPublisher(connectionString);
}
);
var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var client = account.CreateCloudBlobClient();
return client.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("ContainerName"));
}
private void EnsureContainerExists()
{
var container = GetContainer();
container.CreateIfNotExist();
var permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
}
private void SaveImage(string id, string name, string description, string fileName, string contentType, byte[] data)
{
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
try
{
blob.UploadFromStream(fu_upload.FileContent);
blob.Metadata["Id"] = id;
blob.Metadata["Filename"] = fileName;
blob.Metadata["ImageName"] = String.IsNullOrEmpty(name) ? "unknown" : name;
blob.Metadata["Description"] = String.IsNullOrEmpty(description) ? "unknown" : description;
blob.SetMetadata();
lv_images.DataBind();
}
catch (Exception ex)
{ lb_status.Text = ex.Message; }
}
protected void btn_upload_Click(object sender, EventArgs e)
{
if (fu_upload.HasFile)
{
lb_status.Text = "Inserted [" + fu_upload.FileName + "] - Content Type [" +
fu_upload.PostedFile.ContentType + "] - Length [" + fu_upload.PostedFile.ContentLength + "]";
this.SaveImage(Guid.NewGuid().ToString(),
tb_name.Text,
tb_desc.Text,
fu_upload.FileName,
fu_upload.PostedFile.ContentType,
fu_upload.FileBytes
);
RefreshGallery();
}
else
lb_status.Text = "No image file";
}
protected void lv_images_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var metadataRepeater = e.Item.FindControl("blobMetadata") as Repeater;
var blob = ((ListViewDataItem)(e.Item)).DataItem as CloudBlob;
if (blob != null)
{
if (blob.SnapshotTime.HasValue)
{
var delBtn = e.Item.FindControl("deleteBlob") as LinkButton;
if (delBtn != null) delBtn.Text = "Delete Snapshot";
var snapshotBtn = e.Item.FindControl("SnapshotBlob") as LinkButton;
if (snapshotBtn != null) snapshotBtn.Visible = false;
}
if (metadataRepeater != null)
{
metadataRepeater.DataSource = from key in blob.Metadata.AllKeys
select new
{
Name = key,
Value = blob.Metadata[key]
};
metadataRepeater.DataBind();
}
}
}
}
protected void OnDeleteImage(object sender, CommandEventArgs e)
{
try
{
if (e.CommandName == "Delete")
{
var blobUri = (string)e.CommandArgument;
var blob = this.GetContainer().GetBlobReference(blobUri);
blob.DeleteIfExists();
}
}
catch (StorageClientException se)
{
lb_status.Text = "Storage client error: " + se.Message;
}
catch (Exception) { }
RefreshGallery();
}
protected void OnCopyImage(object sender, CommandEventArgs e)
{
if (e.CommandName == "Copy")
{
// Prepare an Id for the copied blob
var newId = Guid.NewGuid();
// получение исходного объекта
var blobUri = (string)e.CommandArgument;
var srcBlob = this.GetContainer().GetBlobReference(blobUri);
// создание нового бинарного объекта
var newBlob = this.GetContainer().GetBlobReference(newId.ToString());
// копирование содержимого исходного объекта
newBlob.CopyFromBlob(srcBlob);
// получаем метаданные для нового объекта
newBlob.FetchAttributes(new BlobRequestOptions { BlobListingDetails = BlobListingDetails.Metadata });
// изменение метаданных нового объекта, чтобы показать, что это копия
newBlob.Metadata["ImageName"] = "Copy of \"" +
newBlob.Metadata["ImageName"] + "\"";
newBlob.Metadata["Id"] = newId.ToString();
newBlob.SetMetadata();
RefreshGallery();
}
}
}
}
В качестве примера рассмотрим работу простого веб - приложения для загрузки изображений в хранилище Windows Azure Blob:
Создайте проект облачной службы и добавьте веб - роль в решение.
В свойствах веб - роли определим строку подключения к эмулятору хранилища Azure.
(рис 20.1) Также нам понадобится строковый параметр ContainerName, определяющий имя Blob - контейнера для изображений.
(рис 20.2) Создадим веб - форму AzureBlobSamle.aspx и добавим ссылки в C# класс данной страницы
using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.StorageClient; using System.Configuration;
Далее, поскольку Blob - контейнер должен быть доступным для анонимных пользователей необходимо создать метод, подтверждающий наличие контейнера:
private void EnsureContainerExists()
{
var container = GetContainer();
container.CreateIfNotExist();
var permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
}
Как видно, в данном методе присутствует ссылка на несуществующий пока GetConteiner() , который должен возвращать Blob-контейнер для осуществления операций с Blob-объектами.
private CloudBlobContainer GetContainer()
{
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSettingPublisher) =>
{
var connectionString =
RoleEnvironment.GetConfigurationSettingValue(configName);
configSettingPublisher(connectionString);
}
);
var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var client = account.CreateCloudBlobClient();
return client.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("ContainerName"));
}
Теперь необходимо заняться самой формой. На форме буду присутствовать текстовые поля, задающие параметры изображения и диалог загрузки файла.
(рис 20.3) asp - код веб формы:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AzureBlobSample.aspx.cs"
Inherits="WebRole1.AzureBlobSample" %>
<!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 runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Font-Bold="True" Font-Size="X-Large"
Text="My Image Gallery - Azure Blob Example"></asp:Label>
<br />
<br />
<table style="width:100%;">
<tr>
<td>
<asp:Label ID="lb_name" runat="server" Text="Название"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_label" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_desc" runat="server" Text="Описание"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_desc" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_file" runat="server" Text="Файл"></asp:Label>
</td>
<td>
<asp:FileUpload ID="fu_upload" runat="server" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Button ID="btn_upload" runat="server" onclick="btn_upload_Click"
Text="Загрузить" />
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_status" runat="server"></asp:Label>
</td>
<td>
<asp:ListView ID="lv_images" runat="server">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</LayoutTemplate>
<EmptyDataTemplate>
<h2>No Data Available</h2>
</EmptyDataTemplate>
<ItemTemplate>
<div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
</li>
</ul>
<img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
</div>
</ItemTemplate>
</asp:ListView>
</td>
</tr>
<tr>
<td>
<br />
</td>
<td>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Отметим, что элемент управления, при помощи которого мы будем отображать имеющиеся изображения - это ListView с идентификатором lv_images. Для того чтобы сделать возможным повторени вышеуказанного шаблона для каждого элемента списка мы использовали Repeater (подробнее см. в списке вспомогательных материалов)
Теперь в метод Page_Load добавим код, который при первой загрузке нашей страницы будет проверять наличие контейнера и выводить сообщение о возможных ошибках и привязывать lv_images к источнику данных, вызовом метода RefreshGallery:
try
{
if (!IsPostBack)
{
this.EnsureContainerExists();
}
this.RefreshGallery();
}
catch (System.Net.WebException we)
{
lb_status.Text = "Network error: " + we.Message;
if (we.Status == System.Net.WebExceptionStatus.ConnectFailure)
{
lb_status.Text += "<br />Please check if the blob service is running at " +
ConfigurationManager.AppSettings["storageEndpoint"];
}
}
catch (StorageException se)
{
Console.WriteLine("Storage service error: " + se.Message);
}
Метод RefreshGallery - привязывает lv_images к источнику данных(контейнеру бинарных объектов):
private void RefreshGallery()
{
lv_images.DataSource =
this.GetContainer().ListBlobs(new BlobRequestOptions()
{
UseFlatBlobListing = true,
BlobListingDetails = BlobListingDetails.All
});
lv_images.DataBind();
}
Запустим приложение и убедимся, что оно выполняется верно.
(рис 20.4)
Необходимо добавить следующую ссылку в AzureBlobSample.cs:
using System.Collections.Specialized;
Создадим метод, сохраняющий бинарный объект и задающий его метаданные:
private void SaveImage(string id, string name, string description, string tags,
string fileName, string contentType, byte[] data)
{
// Создание BLob - объекта в контейнере
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
// определение метаданных, добавление метаданных blob-объекту и загрузка данных
try
{
blob.UploadFromStream(fu_upload.FileContent);
blob.Metadata["Id"] = id;
blob.Metadata["Filename"] = fileName;
blob.Metadata["ImageName"] = String.IsNullOrEmpty(name) ? "unknown" : name;
blob.Metadata["Description"] = String.IsNullOrEmpty(description) ? "unknown" : description;
blob.SetMetadata();
lv_images.DataBind();
}
catch (Exception ex)
{ lb_status.Text = ex.Message; }
}
}
Остается только написать метод - обрабатывающий событие нажатия кнопки btn_upload:
protected void btn_upload_Click(object sender, EventArgs e)
{
if (fu_upload.HasFile)
{
lb_status.Text = "Inserted [" + fu_upload.FileName + "] -
Content Type [" + fu_upload.PostedFile.ContentType + "] -
Length [" + fu_upload.PostedFile.ContentLength + "]";
this.SaveImage(Guid.NewGuid().ToString(),
tb_name.Text,
tb_desc.Text,
fu_upload.FileName,
fu_upload.PostedFile.ContentType,
fu_upload.FileBytes
);
RefreshGallery();
}
else
lb_status.Text = "No image file";
}
Теперь можно проверить функцию загрузки изображения, выбрав его через диалог загрузки файла.
(рис 20.5) Надо сказать, что в таком виде информация об изображении малоинформативная. Blob, как мы знаем, может хранить привязанные к нему метаданные. Добавим в наше решение функционал, позволяющий получать и отображать метаданные, ассоциированные с конкретным изображением.
Для начала, необходимо создать метод обрабатывающий событие ItemDataBound элемента управления lv_images:
protected void lv_images_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var metadataRepeater = e.Item.FindControl("blobMetadata") as Repeater;
var blob = ((ListViewDataItem)(e.Item)).DataItem as CloudBlob;
if (blob != null)
{
if (blob.SnapshotTime.HasValue)
{
var delBtn = e.Item.FindControl("deleteBlob") as LinkButton;
if (delBtn != null) delBtn.Text = "Delete Snapshot";
var snapshotBtn = e.Item.FindControl("SnapshotBlob") as LinkButton;
if (snapshotBtn != null) snapshotBtn.Visible = false;
}
if (metadataRepeater != null)
{
metadataRepeater.DataSource = from key in blob.Metadata.AllKeys
select new
{
Name = key,
Value = blob.Metadata[key]
};
metadataRepeater.DataBind();
}
}
}
}
Теперь запустим наше приложение и получим следующее:
(рис 20.6) Слева от изображения появился список метаданных.
Для начала необходимо добавить следующий asp - код для lv_images:
...
div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
<asp:LinkButton ID="deleteBlob"
OnClientClick="return confirm('Delete image?');"
CommandName="Delete"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Удалить" oncommand="OnDeleteImage" />
...
Это необходимо для формировании кнопки "Delete" в рамках нашего ListView. Кроме того, этот кол содержит ссылку на метод, выполняющийся при нажатии кнопки.
Метод OnDeleteImage:
protected void OnDeleteImage(object sender, CommandEventArgs e)
{
try
{
if (e.CommandName == "Delete")
{
var blobUri = (string)e.CommandArgument;
var blob = this.GetContainer().GetBlobReference(blobUri);
blob.DeleteIfExists();
}
}
catch (StorageClientException se)
{
lb_status.Text = "Storage client error: " + se.Message;
}
catch (Exception) { }
RefreshGallery();
}
Данный метод определяет бинарный объект и удаляет его.
Запустите приложение и протестируйте функцию удаления.
(рис 20.7)
Копирование бинарных объектов также поддерживается Windows Azure. В рамках данного задания мы рассмотрим этот механизм.
Для формирования кнопки "Копировать" в списке имеющихся изображений, добавим следующий asp - код для lv_images :
...
<div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
...
<asp:LinkButton ID="CopyBlob"
OnClientClick="return confirm('Copy image?');"
CommandName="Copy"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Copy" oncommand="OnCopyImage" />
...
Как и в предыдущем задании, создадим метод OnCopyImage:
protected void OnCopyImage(object sender, CommandEventArgs e)
{
if (e.CommandName == "Copy")
{
// Prepare an Id for the copied blob
var newId = Guid.NewGuid();
// получение исходного объекта
var blobUri = (string)e.CommandArgument;
var srcBlob = this.GetContainer().GetBlobReference(blobUri);
// создание нового бинарного объекта
var newBlob = this.GetContainer().GetBlobReference(newId.ToString());
// копирование содержимого исходного объекта
newBlob.CopyFromBlob(srcBlob);
// получаем метаданные для нового объекта
newBlob.FetchAttributes(new BlobRequestOptions { BlobListingDetails = BlobListingDetails.Metadata });
// изменение метаданных нового объекта, чтобы показать, что это копия
newBlob.Metadata["ImageName"] = "Copy of \"" + newBlob.Metadata["ImageName"]
+ "\"";
newBlob.Metadata["Id"] = newId.ToString();
newBlob.SetMetadata();
RefreshGallery();
}
}
Проверим функционал:
(рис 20.8) В случае, если выполнение задания вызвало сложности и затруднения, в приложениях к данной практической работе вы найдете итоговый программный код в том виде, в котором он необходим для последнего задания.
Работа с Windows Azure Blob
Repeater
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="AzureBlobSample.aspx.cs" Inherits="WebRole1.AzureBlobSample" %>
<!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 runat="server">
<title></title>
<style type="text/css">
.style1
{
height: 25px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Font-Bold="True" Font-Size="X-Large"
Text="My Image Gallery - Azure Blob Example"></asp:Label>
<br />
<br />
<table style="width:100%;">
<tr>
<td>
<asp:Label ID="lb_name" runat="server" Text="Название"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_name" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_desc" runat="server" Text="Описание"></asp:Label>
</td>
<td>
<asp:TextBox ID="tb_desc" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td class="style1">
<asp:Label ID="lb_file" runat="server" Text="Файл"></asp:Label>
</td>
<td class="style1">
<asp:FileUpload ID="fu_upload" runat="server" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Button ID="btn_upload" runat="server" onclick="btn_upload_Click"
Text="Загрузить" />
</td>
</tr>
<tr>
<td>
<asp:Label ID="lb_status" runat="server"></asp:Label>
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:ListView ID="lv_images" runat="server"
onitemdatabound="lv_images_ItemDataBound">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</LayoutTemplate>
<EmptyDataTemplate>
<h2>No Data Available</h2>
</EmptyDataTemplate>
<ItemTemplate>
<div class="item">
<ul style="width:40em;float:left;clear:left" >
<asp:Repeater ID="blobMetadata" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %><span><%# Eval("Value") %></span></li>
</ItemTemplate>
</asp:Repeater>
<li>
<asp:LinkButton ID="deleteBlob"
OnClientClick="return confirm('Delete image?');"
CommandName="Delete"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Удалить" oncommand="OnDeleteImage" />
<asp:LinkButton ID="CopyBlob"
OnClientClick="return confirm('Copy image?');"
CommandName="Copy"
CommandArgument='<%# Eval("Uri")%>'
runat="server" Text="Копировать" oncommand="OnCopyImage" />
</li>
</ul>
<img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
</div>
</ItemTemplate>
</asp:ListView>
</td>
</tr>
<tr>
<td>
<br />
</td>
<td>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Configuration;
using System.Collections.Specialized;
namespace WebRole1
{
public partial class AzureBlobSample : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (!IsPostBack)
{
this.EnsureContainerExists();
}
this.RefreshGallery();
}
catch (System.Net.WebException we)
{
lb_status.Text = "Network error: " + we.Message;
if (we.Status == System.Net.WebExceptionStatus.ConnectFailure)
{
lb_status.Text += "<br />Please check if the blob service is running at " +
ConfigurationManager.AppSettings["storageEndpoint"];
}
}
catch (StorageException se)
{
Console.WriteLine("Storage service error: " + se.Message);
}
}
private void RefreshGallery()
{
lv_images.DataSource =
this.GetContainer().ListBlobs(new BlobRequestOptions()
{
UseFlatBlobListing = true,
BlobListingDetails = BlobListingDetails.All
});
lv_images.DataBind();
}
private CloudBlobContainer GetContainer()
{
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSettingPublisher) =>
{
var connectionString =
RoleEnvironment.GetConfigurationSettingValue(configName);
configSettingPublisher(connectionString);
}
);
var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var client = account.CreateCloudBlobClient();
return client.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("ContainerName"));
}
private void EnsureContainerExists()
{
var container = GetContainer();
container.CreateIfNotExist();
var permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
}
private void SaveImage(string id, string name, string description, string fileName, string contentType, byte[] data)
{
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
try
{
blob.UploadFromStream(fu_upload.FileContent);
blob.Metadata["Id"] = id;
blob.Metadata["Filename"] = fileName;
blob.Metadata["ImageName"] = String.IsNullOrEmpty(name) ? "unknown" : name;
blob.Metadata["Description"] = String.IsNullOrEmpty(description) ? "unknown" : description;
blob.SetMetadata();
lv_images.DataBind();
}
catch (Exception ex)
{ lb_status.Text = ex.Message; }
}
protected void btn_upload_Click(object sender, EventArgs e)
{
if (fu_upload.HasFile)
{
lb_status.Text = "Inserted [" + fu_upload.FileName + "] - Content Type [" +
fu_upload.PostedFile.ContentType + "] - Length [" + fu_upload.PostedFile.ContentLength + "]";
this.SaveImage(Guid.NewGuid().ToString(),
tb_name.Text,
tb_desc.Text,
fu_upload.FileName,
fu_upload.PostedFile.ContentType,
fu_upload.FileBytes
);
RefreshGallery();
}
else
lb_status.Text = "No image file";
}
protected void lv_images_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var metadataRepeater = e.Item.FindControl("blobMetadata") as Repeater;
var blob = ((ListViewDataItem)(e.Item)).DataItem as CloudBlob;
if (blob != null)
{
if (blob.SnapshotTime.HasValue)
{
var delBtn = e.Item.FindControl("deleteBlob") as LinkButton;
if (delBtn != null) delBtn.Text = "Delete Snapshot";
var snapshotBtn = e.Item.FindControl("SnapshotBlob") as LinkButton;
if (snapshotBtn != null) snapshotBtn.Visible = false;
}
if (metadataRepeater != null)
{
metadataRepeater.DataSource = from key in blob.Metadata.AllKeys
select new
{
Name = key,
Value = blob.Metadata[key]
};
metadataRepeater.DataBind();
}
}
}
}
protected void OnDeleteImage(object sender, CommandEventArgs e)
{
try
{
if (e.CommandName == "Delete")
{
var blobUri = (string)e.CommandArgument;
var blob = this.GetContainer().GetBlobReference(blobUri);
blob.DeleteIfExists();
}
}
catch (StorageClientException se)
{
lb_status.Text = "Storage client error: " + se.Message;
}
catch (Exception) { }
RefreshGallery();
}
protected void OnCopyImage(object sender, CommandEventArgs e)
{
if (e.CommandName == "Copy")
{
// Prepare an Id for the copied blob
var newId = Guid.NewGuid();
// получение исходного объекта
var blobUri = (string)e.CommandArgument;
var srcBlob = this.GetContainer().GetBlobReference(blobUri);
// создание нового бинарного объекта
var newBlob = this.GetContainer().GetBlobReference(newId.ToString());
// копирование содержимого исходного объекта
newBlob.CopyFromBlob(srcBlob);
// получаем метаданные для нового объекта
newBlob.FetchAttributes(new BlobRequestOptions { BlobListingDetails = BlobListingDetails.Metadata });
// изменение метаданных нового объекта, чтобы показать, что это копия
newBlob.Metadata["ImageName"] = "Copy of \"" +
newBlob.Metadata["ImageName"] + "\"";
newBlob.Metadata["Id"] = newId.ToString();
newBlob.SetMetadata();
RefreshGallery();
}
}
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.