Как устранить ошибку InvalidMd5, возвращенную службой хранилища BLOB-объектов Windows Azure? - PullRequest
3 голосов
/ 03 ноября 2010

Я создаю приложение, которое должно позволить пользователям загружать большие изображения (до 100 МБ) в службу хранилища BLOB-объектов Windows Azure. Прочитав превосходную статью Роба Гиллена об оптимизации выгрузки файлов для Windows Azure, я позаимствовал его подход для параллельной загрузки порций файлов, используя метод CloudBlockBlob.PutBlock () в Параллельно. Для петли (код доступен здесь ).

Проблема, с которой я столкнулся, заключается в том, что всякий раз, когда я пытаюсь загрузить файл, я получаю исключение " InvalidMd5 " из клиента хранилища . Подозревая, что проблема может быть в хранилище для разработчиков, я также попытался запустить код для моей действующей учетной записи хранилища Azure, но я получил ту же ошибку. Глядя на трафик с помощью Fiddler , я вижу, что заголовок " Content-MD5 " установлен в допустимый хэш MD5. В описании ошибки говорится, что « значение MD5, указанное в запросе, недопустимо. Значение MD5 должно быть 128 бит и закодировано в Base64. », но, насколько мне известно, значение, которое я вижу, является отправлено в Fiddler является допустимым (например, a91c588092cedbdb1b82c2d3786fd509 ).

Вот код, который я использую для вычисления хэша (любезно предоставлено Робом Джилленом):

public static string GetMD5HashFromStream(byte[] data)
{
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] retVal = md5.ComputeHash(data);

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < retVal.Length; i++)
    {
        sb.Append(retVal[i].ToString("x2"));
    }
    return sb.ToString();
}

И это фактический вызов PutBlock ():

blob.PutBlock(transferDetails[j].BlockId, new MemoryStream(buff), blockHash, options);

Я также попытался передать хеш следующим образом:

Convert.ToBase64String(Encoding.UTF8.GetBytes(blockHash))

но результат был тот же - ошибка " InvalidMd5 ": (

MD5-хеш, передаваемый в PutBlock () с кодировкой base64 (например, YTkxYzU4ODA5MmNlZGJkYjFiODJjMmQzNzg2ZmQ1MDk = ) и без него (например, a91c588092cedbd2 * 82 *) не может быть получен. * Код Роба , очевидно, работал на него, и я действительно понятия не имею, что может быть причиной проблемы в моем случае. Единственное изменение, которое я сделал в коде Роба, - это изменить метод расширения ParallelUpload (), чтобы он использовал Stream вместо имени файла и динамически определять размер блока в зависимости от размера загружаемого файла.

Пожалуйста, если у кого-нибудь есть идеи, как решить эту проблему, дайте мне знать! Буду очень благодарен! Я уже потерял два дня, борясь с этим.

Ответы [ 2 ]

3 голосов
/ 04 ноября 2010

Роб, спасибо, что предложили помощь и указали на разницу в хешах MD5. Ваш ответ заставил меня задуматься в правильном направлении. Я провел еще целый день, копаясь в этом, но, к счастью (и благодаря вашему замечанию :)), мне наконец удалось решить проблему. Оказалось, что в моем случае было две проблемы:

1) Хэш MD5 : Я заметил, что хеш, который вы вставили в свой ответ, короче того, который я получил, но мне потребовалось некоторое время, чтобы увидеть, что ваш хэш был ровно дважды короче. После некоторых экспериментов я обнаружил, что метод GetMD5HashFromStream () из вашего тестового приложения преобразует хеш 16-байтовый , сгенерированный MD5CryptoServiceProvider , в 32-символьная строка. И именно эта 32-символьная строка вызывала проблему, потому что она была преобразована в Base64 и передана методу PutBlock () , следовательно, в два раза длиннее и, следовательно, недопустимый хэш Служба хранения BLOB-объектов жаловалась. Вот код, с которым я столкнулся:

Оригинал:

public static string GetMD5HashFromStream(byte[] data)
{
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] retVal = md5.ComputeHash(data);

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < retVal.Length; i++)
    {
        sb.Append(retVal[i].ToString("x2"));
    }
    return sb.ToString();
}

и вызов PutBlock ():

// calculate the block-level hash
string blockHash = Helpers.GetMD5HashFromStream(buff);
blob.PutBlock(transferDetails[j].BlockId, new MemoryStream(buff), blockHash, options);

Final:

MD5 md5 = new MD5CryptoServiceProvider();
byte[] blockHash = md5.ComputeHash(buff);
string convertedHash = Convert.ToBase64String(blockHash, 0, 16);
blob.PutBlock(transferDetails[j].BlockId, new MemoryStream(buff), convertedHash, options);

Роб, мне действительно любопытно, как твой код работал в твоем случае, и почему он не работал в моем - это что-то специфическое для установки на моем компьютере, или, возможно, другая версия инструментов Azure (я используя v1.2) ... Пожалуйста, дайте мне знать, если у вас есть идеи.

2) Ошибка в хранилище для разработки : большое количество прочесываний через Интернет привело меня к этой странице , где упоминается неясная, но, по-видимому, известная ошибка в хранилище для разработки:

Если два запроса пытаются загрузить блок для блоба, который еще не существует в хранилище разработки, один запрос создаст BLOB-объект, а другой может вернуть код статуса 409 (Конфликт), с услугами хранения код ошибки BlobAlreadyExists.

Вот что я придумал, чтобы обойти это:

public static bool IsDevelopmentStorageRunning()
{
    return new Microsoft.ServiceHosting.Tools.DevelopmentStorage.DevStore().IsRunning();
}

Вам нужно будет добавить ссылку на Microsoft.ServiceHosting.Tools.dll , которая находится в " C: \ Program Files \ Windows Azure SDK \ v1.2 \ bin"на моей машине. Затем я использую этот метод перед циклом Parallel.For , который обрабатывает куски файла следующим образом:

bool isDevStorageRunning = StorageProxy.IsDevelopmentStorageRunning();
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = isDevStorageRunning ? 1 : 4;
Parallel.For(0, transferDetails.Length, parallelOptions, j => { ... });

Надеюсь, это спасет кого-то от всех неприятностей, через которые я прошел. Роб, еще раз спасибо за помощь:)

3 голосов
/ 04 ноября 2010

tishon,

После просмотра этого поста я вернулся и повторно протестировал свой код, и я думаю, что существует проблема с передачей данных (возможно, с тем, что вы передаете в функцию?).

Одна вещь, которая сразу же выскочила на меня, это хэш md5, который вы предоставили ... во всех случаях, которые я тестировал, мои хэши md5 заканчиваются двумя знаками равенства, как показано ниже (захвачено из fiddler):

Content-MD5: D1Mxthoqhlwm9cC0729mWA ==

Я не специалист по криптографии, но я знаю по работе с идентификаторами блоков для блочных объектов, что если у вас есть недопустимые / опасные символыидентификатор вашего блоба до его преобразования в закодированное в base64 значение, вы получите недопустимые данные и идентификаторы блоков, которые Azure не может интерпретировать.

Если вы хотите, вы можете отправить мне тестовый файл, который вызываетпроблема rob в gillenfamily .net, и я буду рада пройти через это ... может помочь отследить ее.

Rob

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...