CloudBlockBlob.DownloadToStream против DownloadRangeToStream

Попытка использовать пакет SDK Azure ASP.NET для загрузки изображений из хранилища BLOB-объектов.

В другом посте я читал, что DownloadToStream разбивает большие двоичные объекты на более мелкие части и загружает их параллельно, чтобы повысить производительность. Я считаю, что это то, для чего нужен DownloadRangeToStream.

Мне не удалось найти какую-либо документацию или код, подтверждающий это утверждение о DownloadToStream, и я настроен скептически, потому что он имеет такое же время выполнения, как и просто загрузка прямо с URL-адреса большого двоичного объекта (0,5-3 с за загрузку). Вот код для обоих моих методов загрузки, дающий примерно одинаковую производительность.

Использование CloudBlockBlob.DownloadToStream:

private Bitmap DownloadFromBlob(String set) {

    CloudStorageAccount storageAccount = CloudStorageAccount.Parse( CloudConfigurationManager.GetSetting("StorageConnectionString"));

    CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

    CloudBlobContainer container = blobClient.GetContainerReference("templates");

    CloudBlockBlob blockBlob = container.GetBlockBlobReference(set + ".png");

    using (var memoryStream = new MemoryStream()) {
        blockBlob.DownloadToStream(memoryStream);

        return (memoryStream == null) ? null : (Bitmap)Image.FromStream(memoryStream);
    }
}

Использование Image.FromStream:

private Bitmap DownloadImageFromUrl(string url) {
    try {
        using (WebClient client = new WebClient()) {
            byte[] data = client.DownloadData(url);
            using (MemoryStream mem = (data == null) ? null : new MemoryStream(data)) {
                return (data == null || mem == null) ? null : (Bitmap)Image.FromStream(mem);
            }
        }
    } catch (WebException e) {
        return null;
    }
}

Я пытаюсь увеличить время загрузки изображений размером от 0,5 до 12 МБ. Я попытался реализовать свой собственный метод DownloadRangeToStream для этих изображений, код для которого приведен ниже. Нужно ли мне это делать или DownloadToStream уже делает это за меня? Этот метод дает ту же среду выполнения, что и описанный выше метод DownloadFromBlob.

Используя downloadRangeToStream:

private Image getImageFromStream(string set)
    {
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
        CloudConfigurationManager.GetSetting("StorageConnectionString"));

        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

        CloudBlobContainer container = blobClient.GetContainerReference("templates");

        CloudBlockBlob blockBlob = container.GetBlockBlobReference(set + ".png");

        using (MemoryStream ms = new MemoryStream())
        {

            ParallelDownloadBlob(ms, blockBlob);
            return (ms == null) ? null : Image.FromStream(ms);
        }
    }
private static void ParallelDownloadBlob(Stream outPutStream, CloudBlockBlob blob)
    {
        blob.FetchAttributes();
        int bufferLength = 1 * 1024 * 1024;//1 MB chunk
        long blobRemainingLength = blob.Properties.Length;
        Queue<KeyValuePair<long, long>> queues = new Queue<KeyValuePair<long, long>>();
        long offset = 0;
        while (blobRemainingLength > 0)
        {
            long chunkLength = (long)Math.Min(bufferLength, blobRemainingLength);
            queues.Enqueue(new KeyValuePair<long, long>(offset, chunkLength));
            offset += chunkLength;
            blobRemainingLength -= chunkLength;
        }
        Parallel.ForEach(queues,
            new ParallelOptions()
            {
        //Gets or sets the maximum number of concurrent tasks
        MaxDegreeOfParallelism = 10
            }, (queue) =>
            {
                using (var ms = new MemoryStream())
                {
                    blob.DownloadRangeToStream(ms, queue.Key, queue.Value);
                    lock (outPutStream)
                    {
                        outPutStream.Position = queue.Key;
                        var bytes = ms.ToArray();
                        outPutStream.Write(bytes, 0, bytes.Length);
                    }
                }
            });
    }

person Jaked222    schedule 23.01.2017    source источник


Ответы (1)


Насколько я понимаю, и CloudBlockBlob.DownloadToStream, и Image.FromStream отправят только запрос на загрузку потока, вы можете использовать Fiddler для захватить трафик следующим образом:

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

private static void ParallelDownloadBlob(Stream outPutStream, CloudBlockBlob blob)
{
    blob.FetchAttributes();
    int bufferLength = 1 * 1024 * 1024;//1 MB chunk
    long blobRemainingLength = blob.Properties.Length;
    Queue<KeyValuePair<long, long>> queues = new Queue<KeyValuePair<long, long>>();
    long offset = 0;
    while (blobRemainingLength > 0)
    {
        long chunkLength = (long)Math.Min(bufferLength, blobRemainingLength);
        queues.Enqueue(new KeyValuePair<long, long>(offset, chunkLength));
        offset += chunkLength;
        blobRemainingLength -= chunkLength;
    }
    Parallel.ForEach(queues,
        new ParallelOptions()
        {   
            //Gets or sets the maximum number of concurrent tasks
            MaxDegreeOfParallelism = 10
        }, (queue) =>
            {
                using (var ms = new MemoryStream())
                {
                    blob.DownloadRangeToStream(ms, queue.Key, queue.Value);
                    lock (outPutStream)
                    {
                        outPutStream.Position = queue.Key;
                        var bytes = ms.ToArray();
                        outPutStream.Write(bytes, 0, bytes.Length);
                    }
                }
            });
}

Результат:

Кроме того, есть несколько блогов о параллельной загрузке/выгрузке больших двоичных объектов, вы можете обратиться к ним (blog1 и blog2).

person Bruce Chen    schedule 24.01.2017
comment
Спасибо за этот подробный ответ. Пожалуйста, проверьте мой отредактированный вопрос, чтобы увидеть мою реализацию downloadRangeToStream, которая, как ни странно, не дает лучшей производительности, чем метод DownloadFromBlob выше. - person Jaked222; 24.01.2017
comment
Большой двоичный объект, который я должен загрузить, имеет размер около 96 МБ. Вы можете использовать Fiddler для проверки запросов и диапазона байтов для загрузки при использовании paralleldownloadblob. Кроме того, вы можете увеличить длину буфера, чтобы увеличить длину фрагмента, а затем увеличить диапазон байтов для загрузки. - person Bruce Chen; 24.01.2017