Статьи

Хранилище BLOB-объектов Azure – «Указанное недопустимое содержимое блоба или блока»

Если вы загружаете BLOB-объекты, разбивая BLOB-объекты на блоки, и получаете сообщение об ошибке —  Указанное содержимое BLOB-объекта или блока недопустимо , тогда этот пост для вас.

Укороченная версия

Если вы загружаете BLOB-объекты, разбивая BLOB-объекты на блоки, и получаете указанную выше ошибку,  убедитесь, что идентификаторы блоков ваших блоков имеют одинаковую длину . Если идентификаторы блоков ваших блоков имеют разную длину, вы получите эту ошибку.

Длинная версия

Теперь для более длинной версии этого поста  Улыбка. Несколько дней назад я работал с клиентской библиотекой хранилища, особенно с загрузкой больших двоичных объектов, и с одним конкретным большим двоичным объектом я постоянно получал сообщение об ошибке  : указанное содержимое большого двоичного объекта или блока недопустимо . Я пробовал многочисленные комбинации, даже прибегая к REST API напрямую, но безрезультатно. Это случилось только с одним шариком. Кроме того, если я загрузил один и тот же блоб, не разбив его на блоки, все было хорошо. Я был в конце моего ума. Пробовал искать в интернете эту ошибку, но не смог найти окончательного ответа на мою проблему.

После долгих проб и ошибок мне удалось смоделировать ту же проблему на других объектах. Вот как вы можете воссоздать его:

  1. Начните загрузку BLOB-объекта, разбив его на блоки. Для идентификатора блока, давайте сделаем строку длиной 7 символов, например, intValue.ToString («d7»). Это гарантирует, что мои идентификаторы блоков будут «0000001», «0000002»,…, «0000010»… ..
  2. После загрузки одного или двух блоков отмените операцию.
  3. Теперь повторно загрузите BLOB-объект, разделив его на блоки. Однако на этот раз для идентификатора блока давайте сделаем строку длиной 6 символов, например intValue.ToString («d6»).
  4. Вы получите ошибку, как только попытаетесь загрузить 1- й  блок.

Возможные решения

Теперь, когда мы знаем причину этой проблемы, давайте рассмотрим некоторые из возможных решений этой проблемы.

Подожди снаружи

Одним из возможных решений является ожидание. Я знаю, что это плохо, но все же возможное решение. Мы знаем, что служба хранилища BLOB-объектов Windows Azure хранит все незафиксированные блоки в течение 7 дней, и если в течение 7 дней эти незафиксированные блоки не будут зафиксированы, служба хранения удаляет их.

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

Зафиксировать незафиксированные блоки

Возможно, вы могли бы зафиксировать блоки, которые находятся в незафиксированном состоянии, так что, по крайней мере, вы получите BLOB-объект (который не будет тем, который мы хотели бы загрузить в первую очередь). Затем вы можете удалить этот большой двоичный объект и повторно загрузить его, указав идентификаторы блоков одинаковой длины. Чтобы получить список неподтвержденных блоков, если вы используете REST API напрямую, вы можете выполнить операцию « Получить список блоков » и передать «blocklisttype = uncommitted» в качестве одного из параметров строки запроса. Если вы используете клиентскую библиотеку хранилища (при условии, что вы используете клиентскую библиотеку хранилища .Net версии 2.x), вы можете сделать что-то вроде следующего кода:

private static List<string> GetUncommittedBlockIds(CloudBlockBlob blob)
{
    var sasUri = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
    {
        SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5),
        Permissions = SharedAccessBlobPermissions.Read,
    });
    var blobUri = new Uri(string.Format("{0}{1}", blob.Uri, sasUri));
    List<string> uncommittedBlockIds = new List<string>();
    var request = BlobHttpWebRequestFactory.GetBlockList(blobUri, null, null, BlockListingFilter.Uncommitted, null, null);
    //request.Headers.Add("Authorization", 
    using (var resp = (HttpWebResponse)request.GetResponse())
    {
        using (var stream = resp.GetResponseStream())
        {
            var getBlockListResponse = new GetBlockListResponse(stream);
            var blocks = getBlockListResponse.Blocks;
            foreach (var block in blocks.Where(b => !b.Committed))
            {
                uncommittedBlockIds.Add(Encoding.UTF8.GetString(Convert.FromBase64String(block.Name)));
            }
        }
    }
    return uncommittedBlockIds;
}

Несколько вещей, чтобы иметь в виду здесь:

  • Пространство  имен Microsoft.WindowsAzure.Storage.Blob не имеет возможности получить список неподтвержденных блоков. Вам нужно будет использовать  пространство имен Microsoft.WindowsAzure.Storage.Blob.Protocol .
  • Поскольку мы как бы вызываем API REST, выполняя запрос HttpWebRequest, я создал подпись общего доступа в BLOB-объекте, поэтому мне не нужно создавать заголовок «Авторизация».

Получить незафиксированные блоки, чтобы увидеть длину идентификатора блока

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

Загрузите другой блоб с тем же именем, не разбивая его на блоки

Вы также можете загрузить другой BLOB-объект с тем же именем, не разбивая его на блоки. Это может быть нулевой байт. Таким образом, ваш незафиксированный черный список будет очищен. Затем вы можете удалить этот фиктивный блоб и повторно загрузить его.

Несколько слов о блоках

Поскольку мы говорим о блоках, я подумал, что было бы полезно упомянуть о них несколько моментов:

  • Блоки и операции, связанные с блоками, применимы только для «блочных блобов» . Duh !! Вы получите сообщение об ошибке, если попытаетесь выполнить эти операции с «BLOB-страницей».
  • Для загрузки больших BLOB-объектов рекомендуется разбить ваш BLOB-объект на блоки . На самом деле, если размер вашего блоба превышает 64 МБ, вам придется разбить его на блоки.
  • Минимальный размер блока составляет 1 байт, а максимальный размер блока составляет 4 МБ . Рекомендуется выбирать размер блока на основе вашего интернет-соединения и количества параллельных потоков, которые вы хотите использовать для загрузки этих блоков.
  • BLOB-объект может быть разбит на максимум 50000 блоков . Важно помнить об этом ограничении, потому что вам напоминают об этом ограничении, когда вы пытаетесь загрузить 50001- й  блок.
  • Длина всех идентификаторов блоков должна быть одинаковой . Поэтому, если вы используете целочисленное значение для обозначения идентификатора блока, вы должны дополнить это целое значение «0», чтобы получить ту же длину. Таким образом, вы можете сделать что-то вродеint.ToString («d6»).
  • При передаче идентификатора блока в качестве параметра он должен быть закодирован в Base64 .
  • Хотя порядок загрузки блоков не имеет значения, он важен при фиксации списка блоков,  потому что именно тогда блоб создается службой. Например, допустим, вы загружаете большой двоичный объект, разбивая его на 5 блоков (с идентификаторами «000001», «000002», «000003», «000004» и «000005»). Вы можете загрузить эти блоки в любом порядке — 000004, 000001, 000003, 000005, 000002, однако, когда вы фиксируете список блоков, убедитесь, что идентификаторы блоков переданы в правильном порядке, т.е. 000001, 000002, 000003, 000004, 000005.

Резюме

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