Exercise 2: Working with Blobs

In this exercise, you will use the Windows Azure Blob Storage API to create an application that saves and retrieves image data stored as blobs in Windows Azure storage. It consists of a simple image gallery Web site that can display, upload and remove images in Windows Azure storage, and allows you to enter and display related metadata. The application uses a single container to store its image content as blobs.

When you create a blob in Windows Azure, you associate a content type that specifies the format in which the API returns it and allows you to retrieve an image directly from the URL of the corresponding blob.


Task 1 – Retrieving Blob Data from Storage

In this task, you will create an image gallery web page to display images retrieved from Windows Azure storage. The provided solution consists of a web site project with a single page that contains the elements required to display images and enter metadata. You will add the necessary functionality by editing the code-behind file.

  1. Open Microsoft Visual Studio 2010 elevated as Administrator from Start | All Programs | Microsoft Visual Studio 2010 by right-clicking Microsoft Visual Studio 2010 and choosing Run as administrator.
  2. In the File menu, choose Open and then Project/Solution. In the Open Project dialog, browse to Ex02-WorkingWithBlobs\begin in the Source folder of this lab, select the folder for your preferred language and open the begin.sln file.
  3. Update the service definition to define the configuration settings required to access Windows Azure Table storage. To do this, expand the Roles folder of the RDImageGallery project in Solution Explorer, right-click RDImageGallery_WebRole, and then select Properties.

    Figure 1
    Setting role configuration settings


  4. In the Settings tab, click Add Setting and create a ConnectionString type named DataConnectionString. Click the button labeled with an ellipsis and set the connection string to Use development storage.

    Figure 2
    Configuring a storage connection string


    Figure 3
    Storage connection string dialog


  5. Add another setting named ContainerName and set its value to ‘gallery’.

    Figure 4
    Creating a setting for the container name


    Note:
    The container name must be a valid Domain Name System (DNS) name, conforming to the following naming rules:
    - Must start with a letter or number, and can contain only letters, numbers, and dash(-) characters.
    - All letters must be lowercase.
    - Must be from 3 to 63 characters long.
    - A name cannot contain a dash next to a period.


  6. Expand the RDImageGallery_WebRole node in Solution Explorer, then right-click Default.aspx and select View Code to open the code-behind file for the user interface of the image gallery. In the next steps, you will modify this file to add some of the required functionality.
  7. Make sure the following namespace directives exist at the top of the code file. They are for the utility storage classes and for the ServiceRuntime classes.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-01-Namespace-CS)

    C#Copy Code
    using Microsoft.WindowsAzure;
    using Microsoft.WindowsAzure.ServiceRuntime;
    using Microsoft.WindowsAzure.StorageClient;
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-01-Namespace-VB)

    Visual BasicCopy Code
    Imports Microsoft.WindowsAzure
    Imports Microsoft.WindowsAzure.ServiceRuntime
    Imports Microsoft.WindowsAzure.StorageClient
    

  8. For this lab, you need to store the blobs in a public container, so they are visible on the web to normal, anonymous users. In this step, you will ensure that the container specified in ServiceConfiguration.cscfg exists. To do this, add the following method at the bottom of the _Default class.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-02-EnsureContainerExistsMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      private void EnsureContainerExists()
      {
        var container = GetContainer();
        container.CreateIfNotExist();
    
        var permissions = container.GetPermissions();
        permissions.PublicAccess = BlobContainerPublicAccessType.Container;
        container.SetPermissions(permissions);
      }
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-02-EnsureContainerExistsMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Private Sub EnsureContainerExists()
        Dim container = GetContainer()
        container.CreateIfNotExist()
    
        Dim permissions = container.GetPermissions()
        permissions.PublicAccess = BlobContainerPublicAccessType.Container
        container.SetPermissions(permissions)
      End Sub
    End Class
    

  9. Next, you create a utility method to retrieve a reference to the container created by the code in the previous step. This method will be called in almost all operations since the container is involved with all blob operations. Add a method to create the container at the bottom of the _Default class. This method uses the configuration settings you entered in earlier steps.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-03-GetContainerMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      private CloudBlobContainer GetContainer()
      {
        // Get a handle on account, create a blob storage client and get container proxy
        var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
        var client = account.CreateCloudBlobClient();
    
        return client.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("ContainerName"));
      }
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-03-GetContainerMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Private Function GetContainer() As CloudBlobContainer
        ' Get a handle on account, create a blob storage client and get container proxy
        Dim account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString")
        Dim client = account.CreateCloudBlobClient()
    
        Return client.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("ContainerName"))
      End Function
    End Class
    

  10. Insert the following code (shown in bold) in the Page_Load method to initialize the container and refresh the asp:ListView control on the page that displays the images retrieved from storage.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-04-PageLoadMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      protected void Page_Load(object sender, EventArgs e)
      {
        try
        {
          if (!IsPostBack)
          {
            this.EnsureContainerExists();
          }
          this.RefreshGallery();
        }
        catch (System.Net.WebException we)
        {
          status.Text = "Network error: " + we.Message;
          if (we.Status == System.Net.WebExceptionStatus.ConnectFailure)
          {
            status.Text += "<br />Please check if the blob storage service is running at " +
            ConfigurationManager.AppSettings["storageEndpoint"];
          }
        }
        catch (StorageException se)
        {
          Console.WriteLine("Storage service error: " + se.Message);
        }
      }
      ...
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-04-PageLoadMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
    
      Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
        Try
          If Not IsPostBack Then
            Me.EnsureContainerExists()
          End If
            Me.RefreshGallery()
          Catch we As System.Net.WebException
            status.Text = "Network error: " & we.Message
          If we.Status = System.Net.WebExceptionStatus.ConnectFailure Then
              status.Text += "<br />Please check if the blob storage service is running at " & ConfigurationManager.AppSettings("storageEndpoint")
          End If
        Catch se As StorageException
          Console.WriteLine("Storage service error: " & se.Message)
        End Try
      End Sub
    
      ...
    End Class
    

  11. Add the following method at the bottom of the _Default class to bind the images control to the list of blobs available in the image gallery container. The code uses the ListBlobs method in the CloudBlobContainer object to retrieve a collection of IListBlobItem objects that contain information about each of the blobs. The imagesasp:ListView control in the page binds to these objects to display their value.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-05-RefreshGalleryMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      private void RefreshGallery()
      {
        images.DataSource =
          this.GetContainer().ListBlobs(new BlobRequestOptions()
          {
            UseFlatBlobListing = true,
            BlobListingDetails = BlobListingDetails.All
          });
        images.DataBind();
      }
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-05-RefreshGalleryMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Private Sub RefreshGallery()
        Dim options As BlobRequestOptions = New BlobRequestOptions() With
          {.BlobListingDetails = BlobListingDetails.All,
           .UseFlatBlobListing = True}
    
        images.DataSource = Me.GetContainer().ListBlobs(options)
        images.DataBind()
      End Sub
    
    End Class
    

  12. Press F5 to build and run the application. A browser window launches and displays the contents of the image gallery. Note that at this point, the container is empty and the list view displays a “No Data Available” message. In the next task, you will implement the functionality required to store images as blobs in Windows Azure storage.

    Figure 5
    The image gallery application displaying an empty container


    Note:
    If you have not configured the storage settings correctly or if the storage service is not running, an error similar to the one shown below is displayed.


    Figure 6
    An error caused by an invalid Windows Azure storage configuration or a service problem


  13. Press Shift+F5 in Visual Studio to stop debugging and delete the deployment from the development fabric.

Task 2 – Uploading Blob Data to Storage

In this task, you add functionality to the image gallery Web page to enter metadata and upload image files to Windows Azure storage. The page contains text controls that you can use to enter descriptive metadata for the selected image. An asp:FileUpload control on the page retrieves images from disk and posts them to the page, where they are stored in blob storage.

  1. Open the Default.aspx.cs/ Default.aspx.vb file in the Visual Studio text editor. To do this, right-click the Default.aspx file in Solution Explorer and select View Code.
  2. Add a method at the bottom of the page to save images and their metadata as blobs in Windows Azure storage. The method uses the GetBlobReference method in the CloudBlobContainer object to create a blob from the image data array and the metadata properties.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-06-SaveImageMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      private void SaveImage(string id, string name, string description, string tags, string fileName, string contentType, byte[] data)
      {
        // Create a blob in container and upload image bytes to it
        var blob = this.GetContainer().GetBlobReference(name);
    
        blob.Properties.ContentType = contentType;
    
        // Create some metadata for this image
        var metadata = new NameValueCollection();
        metadata["Id"] = id;
        metadata["Filename"] = fileName;
        metadata["ImageName"] = String.IsNullOrEmpty(name) ? "unknown" : name;
        metadata["Description"] = String.IsNullOrEmpty(description) ? "unknown" : description;
        metadata["Tags"] = String.IsNullOrEmpty(tags) ? "unknown" : tags;
    
        // Add and commit metadata to blob
        blob.Metadata.Add(metadata);
        blob.UploadByteArray(data);            
      }
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-06-SaveImageMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
    
      Private Sub SaveImage(ByVal id As String, ByVal name As String, ByVal description As String, ByVal tags As String, ByVal fileName As String, ByVal contentType As String, _
        ByVal data As Byte())
        ' Create a blob in container and upload image bytes to it
        Dim blob = Me.GetContainer().GetBlobReference(name)
    
        blob.Properties.ContentType = contentType
    
        ' Create some metadata for this image
        Dim metadata = New NameValueCollection()
        metadata("Id") = id
        metadata("Filename") = fileName
        metadata("ImageName") = If([String].IsNullOrEmpty(name), "unknown", name)
        metadata("Description") = If([String].IsNullOrEmpty(description), "unknown", description)
        metadata("Tags") = If([String].IsNullOrEmpty(tags), "unknown", tags)
    
        ' Add and commit metadata to blob
        blob.Metadata.Add(metadata)
        blob.UploadByteArray(data)
      End Sub
    End Class
    

  3. Complete the code in the event handler for the Upload Image button by inserting the code (shown in bold below) to upload_Click method.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-07-UploadClickMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      protected void upload_Click(object sender, EventArgs e)
      {
        if (imageFile.HasFile)
        {
          status.Text = "Inserted [" + imageFile.FileName + "] - Content Type [" + imageFile.PostedFile.ContentType + "] - Length [" + imageFile.PostedFile.ContentLength + "]";
    
          this.SaveImage(
            Guid.NewGuid().ToString(),
            imageName.Text,
            imageDescription.Text,
            imageTags.Text,
            imageFile.FileName,
            imageFile.PostedFile.ContentType,
            imageFile.FileBytes
          );
    
          RefreshGallery();
        }
        else
          status.Text = "No image file";
      }
      ...
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-07-UploadClickMethod-VB)

    Visual BasicCopy Code
    Protected Sub upload_Click(ByVal sender As Object, ByVal e As EventArgs)
      If imageFile.HasFile Then
        status.Text = (("Inserted [" & imageFile.FileName & "] - Content Type [") + imageFile.PostedFile.ContentType & "] - Length [") + imageFile.PostedFile.ContentLength.ToString() + "]"
    
        Me.SaveImage(Guid.NewGuid().ToString(), imageName.Text, imageDescription.Text, imageTags.Text, imageFile.FileName, imageFile.PostedFile.ContentType, _
          imageFile.FileBytes)
    
        RefreshGallery()
      Else
        status.Text = "No image file"
      End If
    End Sub
    

    The code retrieves metadata from the text controls and from properties in the asp:FileUpload control on the page, which include the content type of the posted file, its file name, and the array of bytes containing the image data. It then calls the SaveImage method to store the image and its metadata to Windows Azure storage.

  4. Press F5 to build and run the application and open the image gallery page in a browser window.
  5. Enter metadata in the Name, Description and Tags text boxes. To select the image file, click Browse, navigate to Assets\Images in the Source folder of the Lab, and select one of the available images.

    Figure 7
    Entering metadata to store with the image in blob storage


  6. Click Upload Image to post the image to the Web application. The page refreshes and the newly added image displays in the list view. A status message shows the file name, content type and size of the uploaded file. Note that at this point, no metadata is displayed for the image. In the next task, you will implement the functionality required to retrieve and display metadata for blobs stored in Windows Azure.

    Figure 8
    The image gallery showing the uploaded image


  7. In Visual Studio, press Shift+F5 to stop debugging and delete the deployment from the development fabric.

Task 3 – Retrieving Metadata for Blobs in Storage

Blobs can have metadata attached to them. Metadata headers can be set on a request that creates a new container or blob resource, or on a request that explicitly creates a property on an existing resource. In this task, you will add functionality to the image gallery page to retrieve and display metadata associated with images stored in a Windows Azure container.

  1. Add an event handler to retrieve metadata for each blob displayed in the list view control that displays images. To do this, go to Default.aspx, right-click View Designer, select the imagesListView control, and in the Properties Window (you may need to make it visible by right-clicking the control and choosing Properties) click the Events button. Locate the ItemDataBound event in the Data category, type OnBlobDataBound and press <Enter>. Alternatively, you may edit the ASP.NET markup directly to insert the required event handler.

    Figure 9
    Configuring the event handler to display metadata


  2. In the code-behind file, locate the OnBlobDataBound method and insert the following code (shown in bold) that retrieves the properties for each blob bound to the list view and creates a collection that contains name / value pairs for each metadata item found. The collection is then used as a data source for an asp:Repeater control that displays metadata for each image.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-08-OnBlobDataBoundMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      protected void OnBlobDataBound(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 this blob is a snapshot, rename button to "Delete Snapshot"
          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)
            {
              //bind to metadata
              metadataRepeater.DataSource = from key in blob.Metadata.AllKeys
                                            select new
                                            {
                                              Name = key,
                                              Value = blob.Metadata[key]
                                            };
              metadataRepeater.DataBind();
            }
          }
        }
      }
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-08-OnBlobDataBoundMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Protected Sub OnBlobDataBound(ByVal sender As Object, ByVal e As ListViewItemEventArgs)
        If e.Item.ItemType = ListViewItemType.DataItem Then
          Dim metadataRepeater = TryCast(e.Item.FindControl("blobMetadata"), Repeater)
          Dim blob = TryCast(DirectCast((e.Item), ListViewDataItem).DataItem, CloudBlob)
    
          ' If this blob is a snapshot, rename button to "Delete Snapshot"
          If blob IsNot Nothing Then
            If blob.SnapshotTime.HasValue Then
              Dim delBtn = TryCast(e.Item.FindControl("deleteBlob"), LinkButton)
              If delBtn IsNot Nothing Then
                delBtn.Text = "Delete Snapshot"
              End If
    
              Dim snapshotBtn = TryCast(e.Item.FindControl("Snapshot"), LinkButton)
              If snapshotBtn IsNot Nothing Then
                snapshotBtn.Visible = False
              End If
            End If
    
            If metadataRepeater IsNot Nothing Then
              'bind to metadata
              metadataRepeater.DataSource = From key In blob.Metadata.AllKeys _
              Select New With {.Name = key, .Value = blob.Metadata.Get(key)}
              metadataRepeater.DataBind()
            End If
          End If
        End If
      End Sub
      ...
    End Class
    

  3. Press F5 to build and run the application. Note that the list view now displays the metadata for the image that was uploaded in the previous exercise.

    Figure 10
    The image gallery showing metadata retrieved from blob storage


  4. Press Shift+F5 to stop debugging and delete the deployment from the development fabric.

Task 4 – Deleting Blobs from Storage

In this task, you will add functionality to the image gallery Web page to delete blobs containing image data from Windows Azure storage.

  1. Update the image list view to add an asp:LinkButton control that is used to delete images from the gallery container. To do this, right-click Default.aspx, and select View Markup and locate the ItemTemplate for the images asp:ListView control. Uncomment the ASP.NET markup located immediately following the blobMetadata repeater control (shown bolded below).
    HTMLCopy Code
    ...
    <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="Delete" oncommand="OnDeleteImage" />
          ...
        </li>
      </ul>
      <img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
    </div>
    ...
    

  2. Add code (shown in bold) to Default.aspx.cs / Default.aspx.vb to implement the command handler for the deleteBlobasp:LinkButton control. The code verifies if a blob exists in storage and deletes it.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-09-OnDeleteImageMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      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)
        {
          status.Text = "Storage client error: " + se.Message;
        }
        catch (Exception) { }
    
        RefreshGallery();
      }
      ...
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-09-OnDeleteImageMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Protected Sub OnDeleteImage(ByVal sender As Object, ByVal e As CommandEventArgs)
        Try
          If e.CommandName = "Delete" Then
            Dim blobUri = DirectCast(e.CommandArgument, String)
            Dim blob = Me.GetContainer().GetBlobReference(blobUri)
            blob.DeleteIfExists()
          End If
        Catch se As StorageClientException
          status.Text = "Storage client error: " & se.Message
        Catch generatedExceptionName As Exception
        End Try
    
        RefreshGallery()
      End Sub
      ...
    End Class
    

  3. Press F5 to build and run the application.
  4. Upload a few more images from Assets\Images in the Source folder of the Lab and click Delete on any of the images displayed to remove the corresponding blob from storage.

    Figure 11
    Adding and deleting image blobs from storage


  5. Press Shift+F5 to stop debugging and delete the deployment from the development fabric.

Task 5 – Copying Blobs

Windows Azure blob storage has support for making copies of existing blobs. In this task, you will add functionality to the image gallery Web page to copy blobs containing image data from Windows Azure storage that you added earlier.

  1. Update the image list view to add an asp:LinkButton control that is used to copy images from the gallery container. Open the Default.aspx page in Source mode and locate the ItemTemplate for the images asp:ListView control. Uncomment the ASP.NET markup located immediately following the delete blob link button control (shown in bold text below.)
    HTMLCopy Code
    ...
    <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="Delete" oncommand="OnDeleteImage" />
    
    <asp:LinkButton ID="CopyBlob" 
                          OnClientClick="return confirm('Copy image?');"
                          CommandName="Copy" 
                          CommandArgument='<%# Eval("Uri")%>'
                         runat="server" Text="Copy" oncommand="OnCopyImage" />
          ...
        </li>
      </ul>
      <img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
    </div>
    ...
    

  2. Add code (shown in bold) to Default.aspx.cs / Default.aspx.vb to implement the command handler for the copyBlobasp:LinkButton control. The code creates a copy of a blob based on an existing blob. It also updates the “ImageName” attribute in its metadata to reflect that it is a copy.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-10-OnCopyImageMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      protected void OnCopyImage(object sender, CommandEventArgs e)
      {
        if (e.CommandName == "Copy")
        {
          // Prepare an Id for the copied blob
          var newId = Guid.NewGuid();
    
          // Get source blob
          var blobUri = (string)e.CommandArgument;
          var srcBlob = this.GetContainer().GetBlobReference(blobUri);
    
          // Create new blob
          var newBlob = this.GetContainer().GetBlobReference(newId.ToString());
    
          // Copy content from source blob
          newBlob.CopyFromBlob(srcBlob);
    
          // Explicitly get metadata for new blob
          newBlob.FetchAttributes(new BlobRequestOptions { BlobListingDetails = BlobListingDetails.Metadata });
    
          // Change metadata on the new blob to reflect this is a copy via UI
          newBlob.Metadata["ImageName"] = "Copy of \"" + newBlob.Metadata["ImageName"] + "\"";
          newBlob.Metadata["Id"] = newId.ToString();
          newBlob.SetMetadata();
    
          // Render all blobs
          RefreshGallery();
        }
      }
      ...
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-10-OnCopyImageMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Protected Sub OnCopyImage(ByVal sender As Object, ByVal e As CommandEventArgs)
        If e.CommandName = "Copy" Then
          ' Prepare an Id for the copied blob
          Dim newId = Guid.NewGuid()
    
          ' Get source blob
          Dim blobUri = DirectCast(e.CommandArgument, String)
          Dim srcBlob = Me.GetContainer().GetBlobReference(blobUri)
    
          ' Create new blob
          Dim newBlob = Me.GetContainer().GetBlobReference(newId.ToString())
    
          ' Copy content from source blob
          newBlob.CopyFromBlob(srcBlob)
    
          ' Explicitly get metadata for new blob
          newBlob.FetchAttributes(New BlobRequestOptions())
    
          ' Change metadata on the new blob to reflect this is a copy via UI
          newBlob.Metadata("ImageName") = "Copy of """ & newBlob.Metadata("ImageName") & """"
          newBlob.Metadata("Id") = newId.ToString()
          newBlob.SetMetadata()
    
          ' Render all blobs
          RefreshGallery()
        End If
      End Sub
      ...
    End Class
    

  3. Press F5 to build and run the application.
  4. Upload a few more images from Assets\Images in the Source folder of the lab and click Copy on any of the images displayed to make a copy of the corresponding blob from storage.

    Figure 12
    Copying image blobs from storage


  5. Click OK to confirm the copy operation. You should see a copy of the image has been created with ImageName metadata stating it is a copy.

    Figure 13
    Verification


  6. Press Shift+F5 to stop debugging and delete the deployment from the development fabric.

Task 6 – Taking Blob Snapshots

Windows Azure blob storage has support for taking snapshots of blobs. The different between a snapshot and a copy is that snapshots are read-only and the original blob maintains a relationship to its snapshots; blob copies on the other hand are editable. Once a snapshot has been taken for a blob, this source blob can no longer be deleted. Before a source blob can be deleted, all of its snapshots must be deleted first.

In this task, you add functionality to take a snapshot of a blob that contains image data from Windows Azure storage.

  1. Update the image list view to add an asp:LinkButton control that is used to snapshot images from the gallery container. Open the Default.aspx page in Source mode and locate the ItemTemplate for the images asp:ListView control. Uncomment the ASP.NET markup located immediately following the copy blob link button control (shown in bold).
    HTMLCopy Code
    <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="Delete" oncommand="OnDeleteImage" />
    
          <asp:LinkButton ID="CopyBlob" 
                          OnClientClick="return confirm('Copy image?');"
                          CommandName="Copy" 
                          CommandArgument='<%# Eval("Uri")%>'
                          runat="server" Text="Copy" oncommand="OnCopyImage" />
    
    <asp:LinkButton ID="SnapshotBlob" 
                          OnClientClick="return confirm('Snapshot image?');"
                          CommandName="Snapshot" 
                          CommandArgument='<%# Eval("Uri")%>'
                          runat="server" Text="Snapshot" oncommand="OnSnapshotImage" />
        </li>
      </ul>
      <img src="<%# Eval("Uri") %>" alt="<%# Eval("Uri") %>" style="float:left"/>
    </div>
    

  2. Add code (shown in bold) to Default.aspx.cs / Default.aspx.vb to implement the command handler for the snapshotBlobasp:LinkButton control. The code gets the source blob and takes a snapshot of it.

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-11-OnSnapshotImageMethod-CS)

    C#Copy Code
    public partial class _Default : System.Web.UI.Page
    {
      ...
      protected void OnSnapshotImage(object sender, CommandEventArgs e)
      {
        if (e.CommandName == "Snapshot")
        {
          // Get source blob
          var blobUri = (string)e.CommandArgument;
          var srcBlob = this.GetContainer().GetBlobReference(blobUri);
    
          // Create a snapshot
          var snapshot = srcBlob.CreateSnapshot();
    
          status.Text = "A snapshot has been taken for image blob:" + srcBlob.Uri + " at " + snapshot.SnapshotTime;
    
          RefreshGallery();
        }
      }
      ...
    }
    

    (Code Snippet – ExploringWindowsAzureStorage-Ex02-11-OnSnapshotImageMethod-VB)

    Visual BasicCopy Code
    Partial Public Class _Default
      Inherits System.Web.UI.Page
      ...
      Protected Sub OnSnapshotImage(ByVal sender As Object, ByVal e As CommandEventArgs)
        If e.CommandName = "Snapshot" Then
          ' Get source blob
          Dim blobUri = DirectCast(e.CommandArgument, String)
          Dim srcBlob = Me.GetContainer().GetBlobReference(blobUri)
    
          ' Create a snapshot
          Dim snapshot = srcBlob.CreateSnapshot()
    
          status.Text = "A snapshot has been taken for image blob:" + srcBlob.Uri.ToString() + " at " + snapshot.SnapshotTime
    
          RefreshGallery()
        End If
      End Sub
      ...
    End Class
    

  3. Press F5 to build and run the application.
  4. Click Snapshot on any of the images displayed to take a snapshot the corresponding blob from storage.

    Figure 14
    Taking a snapshot of image blobs from storage


  5. Click OK to confirm the snapshot operation. You will see a status update confirming that a snapshot has been taken.
  6. Attempt to delete the original blob from which the snapshot was taken.

    Figure 15
    Cannot Delete Snapshot error


  7. You will see a status update confirms that the blob cannot be deleted.

    Figure 16
    Cannot Delete Snapshot error


    Note:
    To delete a blob that contains snapshots, all of its snapshots must be deleted first.