From e27b7e7647d068739336777efb36b8be30a418b9 Mon Sep 17 00:00:00 2001 From: Tamra Myers Date: Wed, 13 Jul 2016 17:20:20 -0700 Subject: [PATCH] addressed remaining comments, added example per customer request --- BlobStorage/Advanced.cs | 125 +++++++++++++++++++++++++--------- BlobStorage/GettingStarted.cs | 42 +++++++++--- 2 files changed, 125 insertions(+), 42 deletions(-) diff --git a/BlobStorage/Advanced.cs b/BlobStorage/Advanced.cs index e163a0c..963909c 100644 --- a/BlobStorage/Advanced.cs +++ b/BlobStorage/Advanced.cs @@ -36,8 +36,8 @@ public static class Advanced // Prefix for containers created by the sample. private const string ContainerPrefix = "sample-"; - // Variable for saving the user's service property settings. - private static ServiceProperties properties = null; + // Prefix for blob created by the sample. + private const string BlobPrefix = "sample-blob-"; /// /// Calls the advanced samples for Blob storage. @@ -62,7 +62,7 @@ public static async Task CallBlobAdvancedSamples() userServiceProperties = await blobClient.GetServicePropertiesAsync(); // Get a reference to a sample container. - container = CreateSampleContainer(blobClient).Result; + container = await CreateSampleContainer(blobClient); // Call Blob service client samples. await CallBlobClientSamples(blobClient); @@ -162,7 +162,6 @@ private static async Task CallContainerSamples(CloudBlobContainer container) await ManageContainerLeases(container.ServiceClient); } - /// /// Calls samples that demonstrate how to work with blobs. /// @@ -170,6 +169,13 @@ private static async Task CallContainerSamples(CloudBlobContainer container) /// A Task object. private static async Task CallBlobSamples(CloudBlobContainer container) { + // Create a blob with a random name. + CloudBlockBlob blob = await CreateRandomlyNamedBlockBlob(container); + + // Get a reference to the blob created above from the server. + // This call will fail if the blob does not yet exist. + await GetExistingBlobReference(container, blob.Name); + // Create a specified number of block blobs in a flat structure. await CreateSequentiallyNamedBlockBlobs(container, 5); @@ -194,7 +200,6 @@ private static async Task CallBlobSamples(CloudBlobContainer container) await UploadBlobInBlocks(container); } - /// /// Calls shared access signature samples for both containers and blobs. /// @@ -296,7 +301,6 @@ private static async Task ConfigureBlobAnalytics(CloudBlobClient blobClient) } } - /// /// Gets the Blob service stats for the secondary endpoint for an RA-GRS (read-access geo-redundant) storage account. /// @@ -430,7 +434,7 @@ private static async Task ListContainersWithPrefix(CloudBlobClient blobClient, s private static async Task CreateSampleContainer(CloudBlobClient blobClient) { // Name sample container based on new GUID, to ensure uniqueness. - string containerName = ContainerPrefix + Guid.NewGuid().ToString(); + string containerName = ContainerPrefix + Guid.NewGuid(); // Get a reference to a sample container. CloudBlobContainer container = blobClient.GetContainerReference(containerName); @@ -452,7 +456,6 @@ private static async Task CreateSampleContainer(CloudBlobCli return container; } - /// /// Add some sample metadata to the container. /// @@ -477,7 +480,6 @@ private static async Task AddContainerMetadata(CloudBlobContainer container) } } - /// /// Sets the anonymous access level. /// @@ -506,7 +508,6 @@ private static async Task SetAnonymousAccessLevel(CloudBlobContainer container, throw; } } - /// /// Reads the container's properties. @@ -532,7 +533,6 @@ private static void PrintContainerPropertiesAndMetadata(CloudBlobContainer conta Console.WriteLine(); } - /// /// Demonstrates container lease states: available, breaking, broken, and expired. /// A lease is used in each example to delete the container. @@ -562,7 +562,7 @@ private static async Task ManageContainerLeases(CloudBlobClient blobClient) */ // Lease is available on the new container. Acquire the lease and delete the leased container. - container1 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid().ToString()); + container1 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid()); await container1.CreateIfNotExistsAsync(); // Get container properties to see the available lease. @@ -585,7 +585,7 @@ private static async Task ManageContainerLeases(CloudBlobClient blobClient) Case 2: Lease is breaking */ - container2 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid().ToString()); + container2 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid()); await container2.CreateIfNotExistsAsync(); // Acquire the lease. @@ -609,7 +609,7 @@ private static async Task ManageContainerLeases(CloudBlobClient blobClient) Case 3: Lease is broken */ - container3 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid().ToString()); + container3 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid()); await container3.CreateIfNotExistsAsync(); // Acquire the lease. @@ -630,7 +630,7 @@ private static async Task ManageContainerLeases(CloudBlobClient blobClient) Case 4: Lease has expired. */ - container4 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid().ToString()); + container4 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid()); await container4.CreateIfNotExistsAsync(); // Acquire the lease. @@ -651,7 +651,7 @@ private static async Task ManageContainerLeases(CloudBlobClient blobClient) Case 5: Attempt to delete leased container without lease ID. */ - container5 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid().ToString()); + container5 = blobClient.GetContainerReference(LeasingPrefix + Guid.NewGuid()); await container5.CreateIfNotExistsAsync(); // Acquire the lease. @@ -698,7 +698,6 @@ private static async Task ManageContainerLeases(CloudBlobClient blobClient) } } - /// /// Reads the lease properties for the container. /// @@ -756,7 +755,6 @@ private static async Task DeleteContainersWithPrefix(CloudBlobClient blobClient, } } - /// /// Creates a shared access policy on the container. /// @@ -769,6 +767,8 @@ private static async Task CreateSharedAccessPolicy(CloudBlobContainer container, // The access policy provides create, write, read, list, and delete permissions. SharedAccessBlobPolicy sharedPolicy = new SharedAccessBlobPolicy() { + // When the start time for the SAS is omitted, the start time is assumed to be the time when the storage service receives the request. + // Omitting the start time for a SAS that is effective immediately helps to avoid clock skew. SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24), Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.List | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Create | SharedAccessBlobPermissions.Delete @@ -782,7 +782,6 @@ private static async Task CreateSharedAccessPolicy(CloudBlobContainer container, await container.SetPermissionsAsync(permissions); } - /// /// Returns a URI containing a SAS for the blob container. /// @@ -800,7 +799,8 @@ private static string GetContainerSasUri(CloudBlobContainer container, string st // to construct a shared access policy that is saved to the container's shared access policies. SharedAccessBlobPolicy adHocPolicy = new SharedAccessBlobPolicy() { - // Omit SAS start time to avoid clock skew. Start time is assumed to be the time when the service receives the request. + // When the start time for the SAS is omitted, the start time is assumed to be the time when the storage service receives the request. + // Omitting the start time for a SAS that is effective immediately helps to avoid clock skew. SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24), Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List }; @@ -826,7 +826,6 @@ private static string GetContainerSasUri(CloudBlobContainer container, string st return container.Uri + sasContainerToken; } - /// /// Tests a container SAS to determine which operations it allows. /// @@ -968,7 +967,6 @@ private static async Task TestContainerSAS(string sasUri, string blobName, strin #endregion - #region AllBlobTypeSamples /// @@ -1046,7 +1044,6 @@ private static void PrintBlobPropertiesAndMetadata(CloudBlob blob) Console.WriteLine(); } - /// /// Reads the virtual directory's properties. /// @@ -1125,7 +1122,6 @@ private static async Task ListBlobsFlatListing(CloudBlobContainer container, int } } - /// /// Lists blobs in the specified container using a hierarchical listing, and calls this method recursively to return the contents of each /// virtual directory. Reads the properties on each blob or virtual directory returned and writes them to the console window. @@ -1189,9 +1185,78 @@ private static async Task ListBlobsHierarchicalListing(CloudBlobContainer contai #endregion - #region BlockBlobSamples + /// + /// Creates a randomly named block blob. + /// + /// The container. + /// A Task object. + private static async Task CreateRandomlyNamedBlockBlob(CloudBlobContainer container) + { + // Get a reference to a blob that does not yet exist. + // The GetBlockBlobReference method does not make a request to the service, but only creates the object in memory. + string blobName = BlobPrefix + Guid.NewGuid(); + CloudBlockBlob blob = container.GetBlockBlobReference(blobName); + + // For the purposes of the sample, check to see whether the blob exists. + Console.WriteLine("Blob {0} exists? {1}", blobName, await blob.ExistsAsync()); + + try + { + // Writing to the blob creates it on the service. + await blob.UploadTextAsync(string.Format("This is a blob named {0}", blobName)); + } + catch (StorageException e) + { + Console.WriteLine(e.Message); + Console.ReadLine(); + throw; + } + + // Check again to see whether the blob exists. + Console.WriteLine("Blob {0} exists? {1}", blobName, await blob.ExistsAsync()); + + return blob; + } + + /// + /// Gets a reference to a blob by making a request to the service. + /// + /// The container. + /// The blob name. + /// A Task object. + private static async Task GetExistingBlobReference(CloudBlobContainer container, string blobName) + { + try + { + // Get a reference to a blob with a request to the server. + // If the blob does not exist, this call will fail with a 404 (Not Found). + ICloudBlob blob = await container.GetBlobReferenceFromServerAsync(blobName); + + // The previous call gets the blob's properties, so it's not necessary to call FetchAttributes + // to read a property. + Console.WriteLine("Blob {0} was last modified at {1} local time.", blobName, + blob.Properties.LastModified.Value.LocalDateTime); + } + catch (StorageException e) + { + if (e.RequestInformation.HttpStatusCode == 404) + { + Console.WriteLine("Blob {0} does not exist.", blobName); + Console.WriteLine("Additional error information: " + e.Message); + } + else + { + Console.WriteLine(e.Message); + Console.ReadLine(); + throw; + } + } + + Console.WriteLine(); + } + /// /// Creates the specified number of sequentially named block blobs, in a flat structure. /// @@ -1239,7 +1304,6 @@ private static async Task CreateSequentiallyNamedBlockBlobs(CloudBlobContainer c } } - /// /// Creates the specified number of nested block blobs at a specified number of levels. /// @@ -1296,7 +1360,6 @@ private static async Task CreateNestedBlockBlobs(CloudBlobContainer container, s } } - /// /// Gets a reference to a blob created previously, and copies it to a new blob in the same container. /// @@ -1465,7 +1528,7 @@ private static async Task UploadBlobInBlocks(CloudBlobContainer container) rnd.NextBytes(randomBytes); // Get a reference to a new block blob. - CloudBlockBlob blob = container.GetBlockBlobReference("sample-blob-" + Guid.NewGuid().ToString()); + CloudBlockBlob blob = container.GetBlockBlobReference("sample-blob-" + Guid.NewGuid()); // Specify the block size as 256 KB. int blockSize = 256 * 1024; @@ -1551,7 +1614,6 @@ private static async Task UploadBlobInBlocks(CloudBlobContainer container) } } - /// /// Reads the blob's block list, and indicates whether the blob has been committed. /// @@ -1581,7 +1643,6 @@ private static async Task ReadBlockList(CloudBlockBlob blob) Console.WriteLine(); } - /// /// Returns a URI containing a SAS for the blob. /// @@ -1604,7 +1665,8 @@ private static string GetBlobSasUri(CloudBlobContainer container, string blobNam // to construct a shared access policy that is saved to the container's shared access policies. SharedAccessBlobPolicy adHocSAS = new SharedAccessBlobPolicy() { - // Omit SAS start time to avoid clock skew. Start time is assumed to be the time when the service receives the request. + // When the start time for the SAS is omitted, the start time is assumed to be the time when the storage service receives the request. + // Omitting the start time for a SAS that is effective immediately helps to avoid clock skew. SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24), Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Create }; @@ -1629,7 +1691,6 @@ private static string GetBlobSasUri(CloudBlobContainer container, string blobNam return blob.Uri + sasBlobToken; } - /// /// Tests a blob SAS to determine which operations it allows. /// diff --git a/BlobStorage/GettingStarted.cs b/BlobStorage/GettingStarted.cs index 3c610e8..e3c9e97 100644 --- a/BlobStorage/GettingStarted.cs +++ b/BlobStorage/GettingStarted.cs @@ -36,6 +36,9 @@ namespace BlobStorage /// public static class GettingStarted { + // Prefix for containers created by the sample. + private const string ContainerPrefix = "sample-"; + /// /// Calls each of the methods in the getting started samples. /// @@ -60,7 +63,7 @@ public static void CallBlobGettingStartedSamples() private static async Task BasicStorageBlockBlobOperationsAsync() { const string ImageToUpload = "HelloWorld.png"; - string blockBlobContainerName = "demoblockblobcontainer-" + DateTime.Now.Ticks.ToString(); + string containerName = ContainerPrefix + Guid.NewGuid(); // Retrieve storage account information from connection string CloudStorageAccount storageAccount = Common.CreateStorageAccountFromConnectionString(); @@ -70,7 +73,7 @@ private static async Task BasicStorageBlockBlobOperationsAsync() // Create a container for organizing blobs within the storage account. Console.WriteLine("1. Creating Container"); - CloudBlobContainer container = blobClient.GetContainerReference(blockBlobContainerName); + CloudBlobContainer container = blobClient.GetContainerReference(containerName); try { // The call below will fail if the sample is configured to use the storage emulator in the connection string, but @@ -100,7 +103,9 @@ private static async Task BasicStorageBlockBlobOperationsAsync() blockBlob.Properties.ContentType = "image/png"; await blockBlob.UploadFromFileAsync(ImageToUpload); - // List all the blobs in the container + // List all the blobs in the container. + /// Note that the ListBlobs method is called synchronously, for the purposes of the sample. However, in a real-world + /// application using the async/await pattern, best practices recommend using asynchronous methods consistently. Console.WriteLine("3. List Blobs in Container"); foreach (IListBlobItem blob in container.ListBlobs()) { @@ -117,10 +122,14 @@ private static async Task BasicStorageBlockBlobOperationsAsync() Console.WriteLine("5. Create a read-only snapshot of the blob"); CloudBlockBlob blockBlobSnapshot = await blockBlob.CreateSnapshotAsync(null, null, null, null); - // Clean up after the demo - Console.WriteLine("6. Delete block Blob and all of its snapshots"); + // Clean up after the demo. This line is not strictly necessary as the container is deleted in the next call. + // It is included for the purposes of the example. + Console.WriteLine("6. Delete block blob and all of its snapshots"); await blockBlob.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots, null, null, null); + // Note that deleting the container also deletes any blobs in the container, and their snapshots. + // In the case of the sample, we delete the blob and its snapshots, and then the container, + // to show how to delete each kind of resource. Console.WriteLine("7. Delete Container"); await container.DeleteIfExistsAsync(); } @@ -132,7 +141,7 @@ private static async Task BasicStorageBlockBlobOperationsAsync() private static async Task BasicStorageBlockBlobOperationsWithAccountSASAsync() { const string ImageToUpload = "HelloWorld.png"; - string blockBlobContainerName = "demoblockblobcontainer-" + Guid.NewGuid(); + string containerName = ContainerPrefix + Guid.NewGuid(); // Get an account SAS token. string sasToken = GetAccountSASToken(); @@ -147,7 +156,7 @@ private static async Task BasicStorageBlockBlobOperationsWithAccountSASAsync() Console.WriteLine(); // Get the URI for the container. - Uri containerUri = GetContainerUri(blockBlobContainerName); + Uri containerUri = GetContainerUri(containerName); // Get a reference to a container using the URI and the SAS token. CloudBlobContainer container = new CloudBlobContainer(containerUri, accountSAS); @@ -230,7 +239,7 @@ private static async Task BasicStorageBlockBlobOperationsWithAccountSASAsync() private static async Task BasicStoragePageBlobOperationsAsync() { const string PageBlobName = "samplepageblob"; - string pageBlobContainerName = "demopageblobcontainer-" + Guid.NewGuid(); + string containerName = ContainerPrefix + Guid.NewGuid(); // Retrieve storage account information from connection string CloudStorageAccount storageAccount = Common.CreateStorageAccountFromConnectionString(); @@ -240,7 +249,7 @@ private static async Task BasicStoragePageBlobOperationsAsync() // Create a container for organizing blobs within the storage account. Console.WriteLine("1. Creating Container"); - CloudBlobContainer container = blobClient.GetContainerReference(pageBlobContainerName); + CloudBlobContainer container = blobClient.GetContainerReference(containerName); await container.CreateIfNotExistsAsync(); // Create a page blob in the newly created container. @@ -294,7 +303,18 @@ private static Uri GetContainerUri(string containerName) // Retrieve storage account information from connection string CloudStorageAccount storageAccount = Common.CreateStorageAccountFromConnectionString(); - return new Uri(storageAccount.BlobStorageUri.PrimaryUri.OriginalString + containerName); + Uri containerUri; + + if (storageAccount == CloudStorageAccount.DevelopmentStorageAccount) + { + containerUri = new Uri(storageAccount.BlobStorageUri.PrimaryUri.OriginalString + "/" + containerName); + } + else + { + containerUri = new Uri(storageAccount.BlobStorageUri.PrimaryUri.OriginalString + containerName); + } + + return containerUri; } /// @@ -313,6 +333,8 @@ private static string GetAccountSASToken() // Protocols: HTTPS or HTTP (note that the storage emulator does not support HTTPS) SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy() { + // When the start time for the SAS is omitted, the start time is assumed to be the time when the storage service receives the request. + // Omitting the start time for a SAS that is effective immediately helps to avoid clock skew. Permissions = SharedAccessAccountPermissions.Read | SharedAccessAccountPermissions.Write | SharedAccessAccountPermissions.List | SharedAccessAccountPermissions.Create | SharedAccessAccountPermissions.Delete, Services = SharedAccessAccountServices.Blob, ResourceTypes = SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,