Статьи

Новые возможности хранилища Azure: API REST, добавление BLOB-объектов, изменения файловой службы

Я давно писал в блоге о хранилище Azure. Ранее в этом месяце группа хранения Azure выпустила новую версию службы хранилища и включила в себя множество замечательных возможностей! В этом посте я постараюсь обобщить их.

Итак, начнем!

Новая версия REST API службы хранения / Клиентская библиотека

Все новые изменения свернуты в новую версию REST API. Новая версия « 2015-02-21 ». В этой новой версии произошли некоторые существенные изменения, поэтому, если вы хотите использовать новый материал, убедитесь, что вы используете последнюю версию REST API.

В эту версию включен новый тип BLOB-объекта «Добавить блоб» и множество новых функций в файловой службе Azure.

Наряду с новым REST API команда хранилища Azure также выпустила новую версию клиентской библиотеки хранилища — версию 5.0.0. В этой версии реализованы все функции, доступные в последней версии REST API. Вы можете получить эту библиотеку в своих проектах от Nuget: https://www.nuget.org/packages/WindowsAzure.Storage/ .

Добавить Blob

Append Blob — самый новый ребенок на блоке. Ранее в хранилище Azure было доступно два вида больших двоичных объектов: блочные и блочные. Сейчас их три.

Как следует из названия, в приложении «Добавить блоб» содержимое всегда добавляется в блоб. Он идеально подходит для хранения данных регистрации или телеметрии. Несмотря на то, что вы могли бы реализовать такую ​​функциональность и с блочными BLOB-объектами, но добавление BLOB-объектов значительно упрощает сбор данных регистрации.

Давайте рассмотрим сценарий, в котором вы хотите собирать данные журналов из вашего веб-приложения и сохранять их в хранилище больших двоичных объектов. Кроме того, предположим, что вы хотите только один файл в день. То, как вы будете делать это с помощью «Добавить блоб», заключается в том, что вы сначала создаете пустой бланк добавления и, когда данные поступают, вы просто записываете это в блоб. Добавить блоб будет гарантировать, что существующие данные не будут перезаписаны, а новый отправляемый контент будет записан в конец блоба.

Для управления приложением BLOB в библиотеке клиента .Net Storage создается новый класс — CloudAppendBlob [Извините, но документация MSDN еще не обновлена]. То, как вы работаете с CloudAppendBlob, очень похоже на то, как вы работаете с CloudBlockBlob или CloudPageBlob.

        static void CreateEmptyAppendBlob()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var blobClient = account.CreateCloudBlobClient();
            var container = blobClient.GetContainerReference("logs-container");
            container.CreateIfNotExists();
            var logBlob = container.GetAppendBlobReference(DateTime.UtcNow.Date.ToString("yyyy-MM-dd") + ".log");
            logBlob.CreateOrReplace();
            container.DeleteIfExists();
        }
        static void WriteToAppendBlob()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var blobClient = account.CreateCloudBlobClient();
            var container = blobClient.GetContainerReference("logs-container");
            container.CreateIfNotExists();
            var logBlob = container.GetAppendBlobReference(DateTime.UtcNow.Date.ToString("yyyy-MM-dd") + ".log");
            logBlob.CreateOrReplace();
            logBlob.AppendText(string.Format("[{0}] - some log entry", DateTime.UtcNow));
            container.DeleteIfExists();
        }

Добавить BLOB-объект поддерживает все операции, поддерживаемые другими типами BLOB-объектов. Вы можете копировать добавленные BLOB-объекты, делать снимки, просматривать / обновлять метаданные, просматривать / обновлять свойства, загружать и т. Д.

Некоторые важные замечания:

  • Добавить BLOB-объекты поддерживаются только в версии REST API «2015-02-21». Таким образом, если вы хотите использовать Append Blobs, вы должны использовать последнюю версию REST API.
  • Предполагая, что у вас есть контейнер больших двоичных объектов, в котором есть блочные, страничные и добавляемые большие двоичные объекты. Вы должны использовать последнюю версию REST API. Перечисление BLOB-объектов завершится ошибкой на уровне самого REST API, если вы используете более старую версию REST API.

Изменение подписи общего доступа (SAS)

Если вы используете REST API для создания SAS, существует одно серьезное изменение в способе создания «канонизированного ресурса» при создании строки для подписи. В последней версии вы должны добавить имя службы (blob, table, queue или file) к канонизированному ресурсу. Например, если URL-адрес, для которого вы хотите создать SAS, является «https://myaccount.blob.core.windows.net/music»:

В предыдущих версиях канонизированный ресурс был бы:

/myaccount/music

Но в новой версии это будет:

blob/myaccount/music

Подробнее о подписи общего доступа вы можете узнать здесь: https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx .

Изменения файловой службы

Здесь начинается веселье. В Файловой службе сделано несколько изменений. Давайте поговорим о них!

Однако обратите внимание, что файловая служба все еще находится в режиме предварительного просмотра и, следовательно, не включена по умолчанию в вашей учетной записи / подписке хранения. Вам необходимо включить файловую службу в вашей подписке, посетив портал управления учетной записью.

CORS

Как вы, возможно, уже знаете, другие службы хранения (BLOB-объекты, очереди и таблицы) уже давно поддерживают CORS (это, наряду с SAS, стало основой Cloud Portam ). Теперь Файловая служба также поддерживает CORS!

CORS для файловой службы работает так же, как и для других служб:

  • Правила CORS применяются на уровне обслуживания.
  • Для файловой службы может быть не более 5 правил CORS.
  • Каждое правило CORS будет иметь список разрешенных источников, HTTP-глаголов, список разрешенных (запрос) и открытых (ответ) заголовков и максимальный возраст в секундах.

Давайте рассмотрим пример установки правила CORS для файловой службы. В этом примере я буду использовать правило CORS, необходимое для Cloud Portam. Для Cloud Portam нам нужен следующий набор правил CORS:

Разрешенные источники https://app.cloudportam.com Разрешенные глаголы Получить, Заголовок, Разместить, Поместить, Удалить, Трассировка, Параметры, Подключить и объединить Разрешенные заголовки * Открытые заголовки * Максимальный возраст 3600

        static void SetFileServiceCorsRule()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var serviceProperties = new Microsoft.WindowsAzure.Storage.File.Protocol.FileServiceProperties();
            CorsRule corsRule = new CorsRule()
            {
                AllowedOrigins = new List<string>() { "https://app.cloudportam.com"},
                AllowedMethods = CorsHttpMethods.Connect | CorsHttpMethods.Delete | CorsHttpMethods.Get | 
                                    CorsHttpMethods.Head | CorsHttpMethods.Merge | CorsHttpMethods.Options | 
                                    CorsHttpMethods.Post | CorsHttpMethods.Put | CorsHttpMethods.Trace,
                AllowedHeaders = new List<string>() { "*" },
                ExposedHeaders = new List<string>() { "*" },
                MaxAgeInSeconds = 3600
            };
            serviceProperties.Cors.CorsRules.Add(corsRule);
            fileClient.SetServiceProperties(serviceProperties);
        }

Вот пример того, как бы вы прочитали правила CORS, установленные в настоящее время для файловой службы.

        static void GetFileServiceCorsRule()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var serviceProperties = fileClient.GetServiceProperties();
            var corsRules = serviceProperties.Cors.CorsRules;
            foreach (var corsRule in corsRules)
            {
                Console.WriteLine("Allowed Origins: " + string.Join(", ", corsRule.AllowedOrigins));
                List<string> allowedMethods = new List<string>();
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Connect))
                {
                    allowedMethods.Add(CorsHttpMethods.Connect.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Delete))
                {
                    allowedMethods.Add(CorsHttpMethods.Delete.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Get))
                {
                    allowedMethods.Add(CorsHttpMethods.Get.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Head))
                {
                    allowedMethods.Add(CorsHttpMethods.Head.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Merge))
                {
                    allowedMethods.Add(CorsHttpMethods.Merge.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Options))
                {
                    allowedMethods.Add(CorsHttpMethods.Options.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Post))
                {
                    allowedMethods.Add(CorsHttpMethods.Post.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Put))
                {
                    allowedMethods.Add(CorsHttpMethods.Put.ToString());
                }
                if (corsRule.AllowedMethods.HasFlag(CorsHttpMethods.Trace))
                {
                    allowedMethods.Add(CorsHttpMethods.Trace.ToString());
                }
                Console.WriteLine("Allowed Methods: " + string.Join(", ", allowedMethods));
                Console.WriteLine("Allowed Headers: " + string.Join(", ", corsRule.AllowedHeaders));
                Console.WriteLine("Exposed Headers: " + string.Join(", ", corsRule.ExposedHeaders));
                Console.WriteLine("Max Age (in Seconds): " + corsRule.MaxAgeInSeconds);
            }
        }

Поделиться квотой

Теперь вы можете определить квоту на акцию. Квота ограничит максимальный размер этой доли. Значение квоты на акции должно составлять от 1 МБ до 5 ГБ.

Вы можете установить квоту общего ресурса при его создании. Вы также можете обновить квоту позже, изменив свойства общего ресурса.

        static void CreateShareWithQuotaAndUpdateIt()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.Properties.Quota = 128;//Set share quota to 128 MB.
            share.CreateIfNotExists();
            //Fetch the share's attributes
            share.FetchAttributes();
            Console.WriteLine("Share's Quota = " + share.Properties.Quota);
            //Now let's update the share quota
            share.Properties.Quota = 1024;//Set share quota to 1 GB
            share.SetProperties();
            //Fetch the share's attributes
            share.FetchAttributes();
            Console.WriteLine("Share's Quota = " + share.Properties.Quota);
            share.DeleteIfExists();
        }

Обратите внимание, что если вы не установили квоту при создании общего ресурса, ее квота будет установлена ​​на 5 ГБ (т.е. максимальное значение).

Однако если вы вызываете «SetProperties ()» для общего ресурса, но не предоставляете значение для квоты, его значение не изменяется.

Доля использования

Еще одна полезная функция, представленная в хранилище, — это возможность просмотра использования общего ресурса, т. Е. Какая доля квоты была использована. Обратите внимание, что это приблизительное значение.

        static void GetShareUsage()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.Properties.Quota = 128;//Set share quota to 128 MB.
            share.CreateIfNotExists();
            var shareUsage = share.GetStats().Usage;
            Console.WriteLine("Share Usage (in MB): " + shareUsage);
            share.DeleteIfExists();
        }

Поделиться политикой доступа

Еще одна важная функция, которая отсутствовала в файловой службе до сих пор. До текущей версии не было анонимного доступа к общим ресурсам и файлам файловой службы. Чтобы выполнить любую операцию с файловой службой, вам потребуется ключ учетной записи.

С введением поддержки политик общего доступа и подписи общего доступа теперь можно выполнять определенные операции с общими ресурсами и файлами без использования ключа учетной записи.

Общие политики доступа работают так же, как политики доступа к контейнеру BLOB-объектов:

  • Может быть максимум 5 политик доступа на акцию.
  • Каждая политика доступа должна иметь уникальный идентификатор и дополнительно может иметь дату начала / окончания и разрешения (Чтение, Запись, Список и Удалить).
  • При использовании политики доступа для создания подписи общего доступа необходимо указывать только отсутствующие параметры. Например, если в политике доступа определена дата начала, вы не можете указать дату начала в своей подписи общего доступа.

Давайте посмотрим, как вы можете создать политику общего доступа для общего ресурса. В этом примере мы создаем политику доступа со всеми разрешениями (Чтение, Запись, Список и Удалить) и сроком действия 24 часа с текущей даты / времени.

        static void SetShareAccessPolicy()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.CreateIfNotExists();
            var permissions = new Microsoft.WindowsAzure.Storage.File.FileSharePermissions();
            var sharedAccessFilePolicy = new Microsoft.WindowsAzure.Storage.File.SharedAccessFilePolicy()
            {
                Permissions = Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Read | Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Write | 
                                Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.List | Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Delete,
                SharedAccessExpiryTime = new DateTimeOffset(DateTime.UtcNow.AddDays(1))
            };
            var accessPolicyIdentifier = "policy-1";
            permissions.SharedAccessPolicies.Add(new KeyValuePair<string,Microsoft.WindowsAzure.Storage.File.SharedAccessFilePolicy>(accessPolicyIdentifier, sharedAccessFilePolicy));
            share.SetPermissions(permissions);
        }
        static void GetShareAccessPolicy()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.CreateIfNotExists();
            var permissions = share.GetPermissions();
            var accessPolicies = permissions.SharedAccessPolicies;
            foreach (var item in accessPolicies)
            {
                Console.WriteLine("Identifier: " + item.Key);
                var accessPolicy = item.Value;
                Console.WriteLine("Start Time: " + accessPolicy.SharedAccessStartTime);
                Console.WriteLine("Expiry Time: " + accessPolicy.SharedAccessExpiryTime);
                Console.WriteLine("Read Permission: " + accessPolicy.Permissions.HasFlag(Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Read));
                Console.WriteLine("Write Permission: " + accessPolicy.Permissions.HasFlag(Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Write));
                Console.WriteLine("List Permission: " + accessPolicy.Permissions.HasFlag(Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.List));
                Console.WriteLine("Delete Permission: " + accessPolicy.Permissions.HasFlag(Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Delete));
            }
            share.DeleteIfExists();
        }

Подпись общего доступа

Вместе с политиками доступа поставляется подпись общего доступа (SAS). Это еще одно важное улучшение, сделанное в файловой службе. Теперь вы можете создать SAS URL для общих файловых сервисов и файлов.

SAS для файловой службы работает так же, как SAS для контейнеров и больших двоичных объектов:

  • Вы можете создать как SAS без политик доступа (ad-hoc SAS), так и SAS с политиками доступа.
  • Для SAS вы определяете дату / время начала (необязательно), дату / время окончания и, по крайней мере, одно из прав «Чтение», «Запись», «Список» или «Удалить». Если вы используете политику доступа для определения SAS, то вы указываете только те параметры, которых нет в этой политике доступа.
  • При создании SAS для общего ресурса применяются следующие разрешения: «Чтение», «Запись», «Список» и «Удалить», однако при создании SAS для файла в общем ресурсе разрешение «Список» не применяется.

Давайте посмотрим, как вы можете создать SAS для общего ресурса. В этом примере мы создадим специальный SAS с разрешением «Список», срок действия которого истекает через 24 часа после текущей даты / времени.

        static void CreateSasOnShare()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.CreateIfNotExists();
            var sasToken = share.GetSharedAccessSignature(new Microsoft.WindowsAzure.Storage.File.SharedAccessFilePolicy()
                {
                    Permissions = Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.List,
                    SharedAccessExpiryTime = new DateTimeOffset(DateTime.UtcNow.AddDays(1))
                });
            var sasUrl = share.Uri.AbsoluteUri + sasToken;
            Console.WriteLine(sasUrl);
            share.DeleteIfExists();
        }

Теперь давайте посмотрим, как вы создаете SAS для файла в общем ресурсе. В этом примере мы создадим специальный SAS с правом «Чтение», срок действия которого истекает через 24 часа после текущей даты / времени.

        static void CreateSasOnFile()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.CreateIfNotExists();
            var file = share.GetRootDirectoryReference().GetFileReference("myfile.txt");
            file.UploadText("This is sample file!");
            var sasToken = file.GetSharedAccessSignature(new Microsoft.WindowsAzure.Storage.File.SharedAccessFilePolicy()
            {
                Permissions = Microsoft.WindowsAzure.Storage.File.SharedAccessFilePermissions.Read,
                SharedAccessExpiryTime = new DateTimeOffset(DateTime.UtcNow.AddDays(1))
            });
            var sasUrl = file.Uri.AbsoluteUri + sasToken;
            Console.WriteLine(sasUrl);
            //Now let's read this file by making an HTTP Web Request using SAS URL.
            var request = (HttpWebRequest) HttpWebRequest.Create(sasUrl);
            request.Method = "GET";
            using (var response = (HttpWebResponse) request.GetResponse())
            {
                using (var streamReader = new StreamReader(response.GetResponseStream()))
                {
                    var fileContents = streamReader.ReadToEnd();
                    Console.WriteLine(fileContents);
                }
            }
            share.DeleteIfExists();
        }

Метаданные каталога

В предыдущих версиях служба хранилища позволяла определять метаданные для общего ресурса и файла, но не для каталога. В этом выпуске они включили эту функцию. Теперь вы можете определить собственные метаданные для каталога в виде пары ключ / значение. Вы можете установить метаданные при создании каталога и обновлять его позже.

Правила для метаданных в каталоге такие же, как для общего ресурса и файла:

  • Ключ метаданных должен быть действительным идентификатором C #.
  • Размер метаданных не может превышать 8 КБ.

Давайте посмотрим, как вы можете установить метаданные в каталоге.

        static void DirectoryMetadata()
        {
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var fileClient = account.CreateCloudFileClient();
            var share = fileClient.GetShareReference("share-name");
            share.CreateIfNotExists();
            var directory = share.GetRootDirectoryReference().GetDirectoryReference("folder");
            directory.Metadata.Add(new KeyValuePair<string, string>("Key1", "Value1"));
            directory.Metadata.Add(new KeyValuePair<string, string>("Key2", "Value2"));
            directory.CreateIfNotExists();
            //Fetch directory attributes
            directory.FetchAttributes();
            var metadata = directory.Metadata;
            foreach (var item in metadata)
            {
                Console.WriteLine("Key = " + item.Key + "; Value = " + item.Value);
            }
            Console.WriteLine("----------------------------------------");
            //Now let's update the metadata
            directory.Metadata.Add(new KeyValuePair<string, string>("Key3", "Value3"));
            directory.Metadata.Add(new KeyValuePair<string, string>("Key4", "Value4"));
            directory.SetMetadata();
            //Fetch directory attributes
            directory.FetchAttributes();
            metadata = directory.Metadata;
            foreach (var item in metadata)
            {
                Console.WriteLine("Key = " + item.Key + "; Value = " + item.Value);
            }
            share.DeleteIfExists();
        }

Копировать файлы

Это еще одна важная функция, представленная в последнем API. По сути, эта функция обеспечивает функцию асинхронного копирования на стороне сервера для копирования файлов в разные общие ресурсы в пределах или между учетными записями хранения. Более того, теперь вы можете копировать файлы из общих папок файловой службы в контейнеры BLOB-объектов и наоборот.

К сожалению, я немного поиграл с этим, чтобы включить некоторые примеры, но я обновлю этот пост более подробно, когда узнаю больше об этой функциональности.

Список желаний

Несмотря на то, что представленные новые функции очень впечатляют, я думаю, что некоторые вещи отсутствуют в API. Некоторые из предметов из моего списка пожеланий:

  • Возможность рекурсивного перечисления файлов. В настоящее время Файловая служба просто перечисляет файлы и каталоги внутри общего ресурса или каталога. Я хотел бы, чтобы команда хранения включала функциональность, в которой я мог бы перечислить все файлы внутри общего ресурса независимо от иерархии вложенных каталогов.
  • Возможность удаления непустой папки. В настоящее время для удаления папки она должна быть полностью пустой. Я хочу, чтобы команда хранения включала функциональность, в которой я мог бы удалить непустую папку.
  • Возможность копирования папки — в настоящее время функция копирования работает только для файла. Я хочу, чтобы команда хранения включала функцию копирования папки.

Это некоторые предметы из моего списка пожеланий. Если у вас есть список пожеланий, пожалуйста, поделитесь ими, предоставив комментарии ниже.

Резюме

Вот именно для этого поста. Надеюсь, вы нашли этот пост полезным. Если вы обнаружите какие-либо проблемы с сообщением, пожалуйста, дайте мне знать, и я исправлю их как можно скорее.