Вот обновленная версия ответа Джоэля Филлмора:
Вместо создания веб-сайта и использования WorkerRole, Azure теперь имеет возможность запускать "WebJobs". Вы можете запустить любой исполняемый файл по требованию на веб-сайте в том же центре обработки данных, где расположена ваша учетная запись хранения, чтобы установить заголовки кэша или любое другое поле заголовка.
- Создайте одноразовый временный веб-сайт в том же центре обработки данных , что и ваша учетная запись хранения. Не беспокойтесь о близких группах; создать пустой сайт ASP.NET или любой другой простой сайт. Содержание неважно.
- Создайте консольную программу, используя приведенный ниже код, который работает с обновленными API хранилища Azure. Скомпилируйте его для выпуска, а затем заархивируйте исполняемый файл и все необходимые библиотеки DLL в файл .zip.
- Создайте WebJob и загрузите файл .zip с шага # 2.
- Запустите WebJob. Все, что записано на консоль, доступно для просмотра в файле журнала, созданном и доступном на странице управления WebJob.
- Обратите внимание на метод UpdateAzureServiceVersion. По-видимому, по умолчанию хранилище Azure обслуживает неправильно отформатированные ETag, поэтому вы можете захотеть запустить этот код один раз, подробнее см. this
Приведенный ниже код запускает отдельную задачу для каждого контейнера, и в каждом контейнере обновляется около 70 заголовков в секунду. Нет платы за выход.
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
namespace AzureHeaders
{
class Program
{
static StorageCredentials storageCredentials =
new StorageCredentials("azureaccountname", @"azzureaccountkey");
private static string newCacheSettings = "public, max-age=7776000"; // 3 months
private static string[] containersToProcess = { "container1", "container2" };
static void Main(string[] args)
{
var account = new CloudStorageAccount(
storageCredentials,
false /* useHttps */);
CloudBlobClient blobClient = account.CreateCloudBlobClient();
var tasks = new List<Task>();
foreach (var container in blobClient.ListContainers())
{
if (containersToProcess.Contains(container.Name))
{
var c = container;
tasks.Add(Task.Run(() => FixHeaders(c)));
}
}
Task.WaitAll(tasks.ToArray());
}
private static async Task FixHeaders(CloudBlobContainer cloudBlobContainer)
{
int totalCount = 0, updateCount = 0, errorCount = 0;
Console.WriteLine("Starting container: " + cloudBlobContainer.Name);
IEnumerable<IListBlobItem> blobInfos = cloudBlobContainer.ListBlobs(useFlatBlobListing: true);
foreach (var blobInfo in blobInfos)
{
try
{
CloudBlockBlob blockBlob = (CloudBlockBlob)blobInfo;
var blob = await cloudBlobContainer.GetBlobReferenceFromServerAsync(blockBlob.Name);
blob.FetchAttributes();
// set cache-control header if necessary
if (blob.Properties.CacheControl != newCacheSettings)
{
blob.Properties.CacheControl = newCacheSettings;
blob.SetProperties();
updateCount++;
}
}
catch (Exception ex)
{
// Console.WriteLine(ex.Message);
errorCount++;
}
totalCount++;
}
Console.WriteLine("Finished container: " + cloudBlobContainer.Name +
", TotalCount = " + totalCount +
", Updated = " + updateCount +
", Errors = " + errorCount);
}
// http://geekswithblogs.net/EltonStoneman/archive/2014/10/09/configure-azure-storage-to-return-proper-response-headers-for-blob.aspx
private static void UpdateAzureServiceVersion(CloudBlobClient blobClient)
{
var props = blobClient.GetServiceProperties();
props.DefaultServiceVersion = "2014-02-14";
blobClient.SetServiceProperties(props);
}
}
}