Windows Azure Blob Storage leicht gemacht – Schritt für Schritt zu einer kleinen Beispielanwendung

Erstaunlicherweise gibt es für Windows Azure Blob Storage kaum leicht verdauliche Beispielanwendungen, die zeigen, wie man leicht auf diesen hoch-skalierbaren, hoch-verfügbaren Cloud Storage zugreifen kann. Diesem Mangel möchte ich mit diesem Blog-Artikel beseitigen.

In den folgenden Schritten soll eine kleine Beispielanwendung erstellt werden, mit deren Hilfe Dokumente in Blob Storage geschrieben, von dort ausgelesen, tabellarisch aufgelistet und auch wieder gelöscht werden können. Der Sourcecode der fertigen Solution steht unten zum Download zur Verfügung. Des weiteren können auch Videos, die ich beim Erstellen der Anwendung gedreht habe heruntergeladen werden:

Die Beispielanwendung geht davon aus, dass Sie ein Cloud Projekt wie in einem meinem letzten Blog-Posts beschrieben angelegt haben.

Öffnen Sie – durch Doppelklick auf die Web Role – die Konfigurationsseite der Web Role. Es erschein die in Abbildung 1 gezeigte Eingabemaske.

image

Abbildung 1: Konfiguration des Storage Accounts (Development Storage)

Geben sie in dieser Maske die in Tabelle 1 aufgelisteten Parameter ein und speichern Sie die WebRole.

Parameter Wert Erläuterung
WAStorageConnectionString UseDevelopmentStorage=true Definiert den zu verwendenden Storage Account. Für Testzwecke kann auch (wie hier) der Development Storage verwendet werden.
WAContainerName democontainer Name des Containers im Storage Account. Beachten Sie, dass der Name nur aus Kleinbuchstaben bestehen darf.

Tabelle 1: Parameter für den Storage Account und die Tabelle

Öffnen Sie nun die Datei Global.asax.cs. Fügen Sie am Anfang der Datei Referenzen auf folgende Namespaces hinzu:

 ...
 using Microsoft.WindowsAzure;
 using Microsoft.WindowsAzure.ServiceRuntime;
 using Microsoft.WindowsAzure.StorageClient;´
 ...

Damit machen Sie dem Programm Klassen zum Zugriff auf die Service-Konfiguration sowie den Windows Azure Storage zugänglich.

Erweitern Sie nun die Methode Application_Start wie im folgenden Listing angegeben.

 void Application_Start(object sender, EventArgs e)
 {
     CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
     {
         configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
     });
 
     var account = 
         CloudStorageAccount.FromConfigurationSetting("WAStorageConnectionString");
 
     var container = account.CreateCloudBlobClient().GetContainerReference(
         RoleEnvironment.GetConfigurationSettingValue("WAContainerName"));
     container.CreateIfNotExist();
 
     var permissions = container.GetPermissions();
     permissions.PublicAccess = BlobContainerPublicAccessType.Container;
     container.SetPermissions(permissions);
 }

Legen Sie im Hauptverzeichnis des Projekts einen Ordner Model an und fügen sie diesem eine neue Klassendatei Image.cs hinzu. Implementieren Sie die Klasse wie folgt:

 namespace WebRole
 {
     public class Image
     {
         public string Title { get; set; }
         public string FileName { get; set; }
         public string Description { get; set; }
         public string Tags { get; set; }
         public string ImageUri { get; set; }
         public long Size { get; set; }
     }
 }

Über diese Klasse werden später Blob-Objekte instanziiert und dient als Basis für die Datenquelle, die über die Oberfläche für die Blob-Zugriffe angesprochen wird. Sie definiert die für Blobs benötigten Attribute.

Legen Sie mit der Klasse ImageDataSource nun die entsprechende Datenquelle an und implementieren Sie die Klasse wie folgt:

 using System;
 using System.Collections.Generic;
 using Microsoft.WindowsAzure.StorageClient;
 using Microsoft.WindowsAzure;
 using Microsoft.WindowsAzure.ServiceRuntime;
 using System.Collections.Specialized;
 
 namespace WebRole
 {
     public class ImageDataSource
     {
         private CloudBlobContainer container = null;
 
         public ImageDataSource()
         {
             var account = 
                 CloudStorageAccount.FromConfigurationSetting("WAStorageConnectionString");
             var client = account.CreateCloudBlobClient();
 
             container = client.GetContainerReference(
                 RoleEnvironment.GetConfigurationSettingValue("WAContainerName"));
         }
 
         public Uri Insert(string title, string description, string tags,
                           string fileName, string contentType, byte[] data)
         {
             var blob = container.GetBlobReference(fileName);
             blob.Properties.ContentType = contentType;
 
             var metadata = new NameValueCollection();
             metadata["FileName"] = fileName;
             metadata["ImageTitle"] = title;
             metadata["Description"] = description;
             metadata["Tags"] = tags;
 
             blob.Metadata.Add(metadata);
             blob.UploadByteArray(data);
 
             return blob.Uri;
         }
 
         public IEnumerable<Image> Select()
         {
             var imageList = new List<Image>();
             var blobList = container.ListBlobs(new BlobRequestOptions());
 
             foreach (CloudBlob blob in blobList)
             {
                 blob.FetchAttributes();
 
                 var image = new Image()
                 {
                     ImageUri = blob.Uri.ToString(),
                     Title = blob.Metadata.Get("ImageTitle"),
                     FileName = blob.Metadata.Get("FileName"),
                     Description = blob.Metadata.Get("Description"),
                     Tags = blob.Metadata.Get("Tags"),
                     Size = blob.Properties.Length
                 };
 
                 imageList.Add(image);
             }
 
             return imageList;
         }
 
         public void Delete(string ImageUri)
         {
             var blob = container.GetBlobReference(ImageUri);
             blob.DeleteIfExists();
         }
     }
 }

Die Klasse stellt also alle Methoden bereit, die zum Auslesen, Hinzufügen und Löschen von Blobs benötigt werden. Kompilieren Sie nun das Projekt. Dies ist wichtig, da andernfalls die Klasse später nicht Auswahl als Datenquelle für den GridView angeboten wird.

Öffnen Sie nun die Datei Default.aspx. Fügen Sie der Datei ein GridView hinzu, indem sie ein entsprechendes Element aus der Toolbox auf die Datei ziehen. Die Datei sollte dann wie in Abbildung 2 gezeigt aussehen.

image

Abbildung 2: Hinzufügen eines GridView-Controls und Auswahl der Datenquelle

Bestimmen Sie nun im Kontextmenü des GridViews (zu sehen in Abbildung 2) die Datenquelle. Wählen Sie den Menüpunkt Choose Data Source / New data source. Es erscheint die in Abbildung 3 gezeigte Eingabemaske.

image

Abbildung 3: Auswahl einer ObjectDataSource

Wählen Sie hier als Datenquelle Object und benennen die Datenquelle mit ObjectDataSource. Bestätigen Sie Ihre Eingabe mit OK. Es erscheint die in Abbildung 4 gezeigte Eingabemaske.

image

Abbildung 4: Auswahl des Business Objekts

Wählen Sie hier als Business-Objekt die Klasse WebRole.ImageDataSource und bestätigen Sie Ihre Eingabe mit Next.

image

Abbildung 5: Auswahl der Business Methoden für Auswahl und Löschen

Bestimmen Sie nun die Methoden für die einzelnen Datenoperationen, d.h. für die Operation Select die Methode Select(), für Insert die Methode Insert() usw. Schließen Sie Ihre Eingabe mit Finish ab. Mit diesen Eingaben stehen dem GridView alle Informationen für die Anzeige der Kontaktdaten zur Verfügung. Wenn Sie im Kontextmenü des GridViews auch noch die Option EnableDeleting aus.

Legen Sie im Code des GridView das Schlüsselattribut für einzelne Image-Objekte, ImageUri wie folgt fest:

 ...
 <asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
     <asp:GridView
         ID="GridView1"
         runat="server"
         AutoGenerateColumns="False" 
         DataSourceID="ObjectDataSource"
         DataKeyNames="ImageUri">
 ...

Konfigurieren Sie die Spalten des GridView wie folgt.

 ...
 <Columns>
     <asp:HyperLinkField DataNavigateUrlFields="ImageUri" DataTextField="FileName" 
         HeaderText="Filename" />
     <asp:BoundField DataField="Title" HeaderText="Title" 
         SortExpression="Title" />
     <asp:BoundField DataField="Description" HeaderText="Description" 
         SortExpression="Description" />
     <asp:BoundField DataField="Tags" HeaderText="Tags" SortExpression="Tags" />
     <asp:BoundField DataField="Size" HeaderText="Size" SortExpression="Size" />
     <asp:CommandField ShowDeleteButton="true"  />
 </Columns>
 ...

Damit verlinken Sie beispielsweise den Namen des Blobs in der Spalte Filename mit dem eigentlichen Blob-Inhalt. Wenn sie also auf den Namen des Bildes klicken, wird das Bild selbst angezeigt.

Konfigurieren Sie nun noch das Eingabeformular zum Upload von Blobs wie im folgenden Listing angegeben.

 ...
     </asp:ObjectDataSource>
     <br />
     <asp:Label ID="filePathLabel" Text="File Path:" runat="server" />
     <asp:FileUpload ID="imageFile" runat="server" />
     <br />
     <asp:Label ID="imageTitleLabel" Text="Image Title:" runat="server" />
     <asp:TextBox ID="imageTitle" runat="server" />
     <br />
     <asp:Label ID="imageDescriptionLabel" Text="Image Description:" runat="server" />
     <asp:TextBox ID="imageDescription" runat="server" />
     <br />
     <asp:Label ID="imageTagsLabel" Text="Image Tags:" runat="server" />
     <asp:TextBox ID="imageTags" runat="server" />
     <br />
     <br />
     <asp:Button ID="insertButton" Text="Submit" runat="server" 
                 OnClick="insertButton_Click" />
 </asp:Content>
 ...

Damit ist die Programmierung der Oberfläche abgeschlossen. Es fehlt noch die Logik für die Default.aspx.cs. Implementieren Sie diese wie im folgenden Listing angegeben:

 using System;
 
 namespace WebRole
 {
     public partial class _Default : System.Web.UI.Page
     {
         private ImageDataSource dataSource = null;
 
         protected void Page_Load(object sender, EventArgs e)
         {
             dataSource = new ImageDataSource();
         }
 
         protected void insertButton_Click(object sender, EventArgs e)
         {
             if (imageFile.HasFile)
             {
                 dataSource.Insert(
                     imageTitle.Text,
                     imageDescription.Text,
                     imageTags.Text,
                     imageFile.FileName,
                     imageFile.PostedFile.ContentType,
                     imageFile.FileBytes
                 );
 
                 GridView1.DataBind();
             }
         }
     }
 }

Starten Sie einen Testlauf, indem Sie den Menüpunkt Debug / Start Debugging auswählen. Nach einem kurzen Moment sollte die Startseite wie in Abbildung 6 zu sehen angezeigt werden.

image

Abbildung 6: Erstmalige Ausführung der Blob Storage Anwendung

Da im Container noch keine Blobs liegen, wird nur das Eingabeformular, nicht jedoch das Grid angezeigt. Wählen Sie über den Button Browse eine Datei aus und vergeben Sie Titel, Beschreibung und Tags. Bestätigen Sie Ihre eingabe mit Submit. Nach erfolgreichem Upload sollte die GridView mit dem Blobeintrag wie in Abbildung 7 zu sehen angezeigt werden.

image

Abbildung 7: Anwendung, nachdem ein Bild hinzugefügt wurde