2016-10-11 7 views
3

Чтобы поддерживать загрузку больших (на самом деле очень больших, до нескольких гигабайт) файлов с отчетом о проделанной работе, мы начали использовать HttpClient с помощью PushStreamContent, как описано here. Он работает просто, мы копируем байты между двумя потоками, вот пример кода:HttpClient выбрасывает исключение OutOfMemory, когда TransferEncodingChunked не установлен

private void PushContent(Stream src, Stream dest, int length) 
    { 
     const int bufferLength = 1024*1024*10; 
     var buffer = new byte[bufferLength]; 
     var pos = 0; 
     while (pos < length) 
     { 
      var bytes = Math.Min(bufferLength, length - pos); 
      src.Read(buffer, 0, bytes); 
      dest.Write(buffer, 0, bytes); 
      pos += bufferLength; 
      dest.Flush(); 
      Console.WriteLine($"Transferred {pos} bytes"); 
     } 
     dest.Close(); 
    } 

Но в начале этого код поднял OutOfMemory исключения после передачи 320 МБ, даже если потребление памяти процесса не было очень высоким (около 500 МБ). Что устранил эту проблему устанавливает флаг TransferEncodingChunked:

request.Headers.TransferEncodingChunked = true; 

Не только мы были в состоянии передавать большие файлы с этим флагом, потребление памяти сократилось на 90%.

Я не нашел документацию, требующую использования TransferEncodingChunked, это был скорее процесс проб и ошибок, но в этом сценарии он имеет решающее значение. Тем не менее я озадачен тем, почему исключение вообще выбрано - потребление памяти не очень велико, что вызывает его?

+0

Ну, если данные большие, лучше отправить их в куски, вы находите это удивительным? – demonplus

+0

Я нахожу удивительное исключение OutOfMemory, поднятое так рано. –

ответ

3

Chunked transfer encoding

фрагментированное кодирование передачи представляет собой механизм передачи данных в версии 1.1 протокола передачи гипертекста (HTTP), в котором данные передаются в серии «куски» . Он использует HTTP-заголовок Transfer-Encoding на месте заголовка Content-Length, который в противном случае требовал бы более ранняя версия протокола . 1 Поскольку заголовок Content-Length не используется, отправителю не требуется знать длину содержимого , прежде чем он начнет передавать ответ получателю. Отправители могут начать передачу динамически генерируемого контента до , зная общий размер этого контента.

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

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

+0

Это имеет смысл и объясняет, почему потребление памяти снижается. Тем не менее странно, что исключение OutOfMemory выбрано вообще. Если я не использую PushStreamContent и придерживаюсь tranditional StreamContent, полностью контролируемого HttpClient, никаких исключений не возникает. –

+1

@VagifAbilov Я не могу сказать вам, почему это исследование нуждается, и это другой вопрос. Вы можете проверить этот вопрос: http://stackoverflow.com/questions/16168683/webapi-streamcontent-vs-pushstreamcontent. В значительной степени они сказали, что контент потокового потока используется, когда вам нужно переместить данные в поток, и Stream Content, когда вам нужно вытащить из потока. – mybirthname

+0

Спасибо за ссылку, весьма полезен. –