Как включить поиск в потоке BLOB-объектов Azure

У меня есть BlobStream, созданный методом OpenWriter.

var blob = CloudContainer.GetBlobReference(name));
if (blob == null)
{
   return null;
}

return blob.OpenWrite();

Используя этот поток, я хотел бы найти или установить позицию, но каждый раз, когда я это делаю, я получаю исключение NotSupportedException. Проведя некоторое исследование, я обнаружил, что для canSeek установлено значение false, что и вызывает эту проблему. Но CanSeek ложен, только если длина неизвестна. Но длина известна, когда я запускаю отладчик.

Почему CanSeek неверен? Как я могу установить его в true?


person Stian Standahl    schedule 11.05.2013    source источник
comment
Какую версию клиентской библиотеки вы используете? Какого типа базовый большой двоичный объект (блок или страница)? В последнем клиенте версии 2.0 похоже, что вы можете искать поток записи только в больших двоичных объектах страницы, поэтому это может быть просто невозможно.   -  person Brian Reischl    schedule 11.05.2013
comment
я использую блочный блоб.   -  person Stian Standahl    schedule 11.05.2013
comment
есть ли большие различия между блоком и страницей?   -  person Stian Standahl    schedule 11.05.2013
comment
Я быстро перешел на страницу, чтобы посмотреть, работает ли она. Я буду держать вас в курсе.   -  person Stian Standahl    schedule 11.05.2013
comment
Метод OpenWrite не поддерживается страничными BLOB-объектами.   -  person Stian Standahl    schedule 11.05.2013
comment
Я мало работал с блобами страниц, но ясно, что вы можете писать в BLOB-объекты страниц, иначе как бы вы их создали? BlobWriteStreamBase имеет явную поддержку записи и поиска страничных BLOB-объектов. Было бы полезно, если бы вы указали, какую версию клиентской библиотеки вы используете, и опубликовали код, который вы используете для создания большого двоичного объекта.   -  person Brian Reischl    schedule 11.05.2013
comment
Я обнаружил, что поиск в страничном BLOB-объекте возможен. Но писать пакетами по 512 байт — дополнительная работа. Что я сделал, так это создал поток из BLOB-объекта и скопировал его в поток памяти для манипуляций. На самом деле это не рекомендуется, так как чтение и запись большого двоичного объекта выполняется медленно, если вам нужно приложение, ориентированное на производительность. Но ваш ответ @BrianReischl - хороший ответ. Если вы напишете что-то для ответа, я отмечу это как правильное :)   -  person Stian Standahl    schedule 16.05.2013


Ответы (2)


Вы можете выполнять поиск в страничном BLOB-объекте — для этого есть явная поддержка в BlobWriteStreamBase.

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

person Brian Reischl    schedule 16.05.2013
comment
Существуют ограничения на поиск с помощью страничного BLOB-объекта. Поправьте меня, если я ошибаюсь, но я думаю, что вы можете читать и писать только 512 байт за раз. Это означает, что поиск должен выполняться для каждой позиции с приращением 512. (512*x, где x — целое число). - person Stian Standahl; 21.05.2013

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

При возврате controller.File(stream, contentType, filename, true) в контроллер, начиная с последнего параметра enableRangeProcessing=true, он проверяет stream.CanSeek=true и вызывает stream.Seek со значением range.from входящего заголовка contentrangeheader.

Так что оберните источник return new SeekableBlobReadStream(blobclient) и отправьте его контроллеру.

internal class SeekableBlobReadStream : Stream
{
    private Stream stream;
    private readonly BlobBaseClient client;
    private readonly BlobProperties properties;

    public SeekableBlobReadStream(BlobBaseClient client)
    {
        properties = client.GetProperties().Value;
        this.client = client;
    }

    private void PrepareStream(long position = 0)
    {
        if (stream == null || position != 0)
        {
            if (stream != null)
                stream.Dispose();
            stream = client.OpenRead(new BlobOpenReadOptions(false) { Position = position });
        }
    }

    public override bool CanRead => true;

    public override bool CanSeek => true;

    public override bool CanWrite => false;

    public override long Length => properties.ContentLength;

    public override long Position
    {
        get => (stream?.Position).GetValueOrDefault();
        set => PrepareStream(value);
    }

    public override void Flush()
    {
        PrepareStream();
        stream.Flush();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        PrepareStream();
        return stream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Current:
                offset = Position + offset;
                break;
            case SeekOrigin.End:
                offset = Length - offset;
                break;
        }
        PrepareStream(offset);
        return stream.Position;
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }
}
person Balaji Gunasekaran    schedule 09.09.2020
comment
Это отличное решение, спасибо, что поделились им. - person MoonKnight; 12.07.2021