Передача больших файлов: объединение потоковой передачи и длины содержимого

TL; DR: как я могу передавать большие файлы с известным размером с помощью WCF и все еще показывать прогресс (Content-Length) конечному пользователю (веб-браузер)?

у меня есть служба WCF REST, которая загружает, а затем обслуживает очень большие файлы (1-20Gb) в веб-браузере. Чтобы упростить, подумайте о моем сервисе как о прокси. Это обязывает меня установить TransferMode = Streamed или TransferMode = StreamedResponse на привязке, или конечному клиенту придется подождать, пока исходные файлы будут загружены на веб-сервер до фактической загрузки начинает. Кроме того, буферизованный режим передачи убивает сервер для больших файлов (использование ОЗУ). Промежуточное дисковое хранилище не является вариантом. От TransferMode man page:

(...) Буферизованные передачи удерживают все сообщение в буфере памяти до передача завершена.

но при установке TransferMode to Streamed или StreamedResponse, WCF больше не возвращает заголовок Content-length клиенту, и новый заголовок это. Это согласуется с статья wikipedias на chunked передачи:

(...) использует HTTP-заголовок Transfer-Encoding на месте контент-длина заголовок.( ..)

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

(как) можно настроить привязку WCF для использования режима" потоковой передачи" (более конкретно, не буферизация всего сообщения перед отправкой) и по-прежнему использовать ?

некоторые выводы:

  • это q / a заявляет, что стандарт http запрещает оба Transfer-Encoding и Content-length: 123456 в том же сообщении, так что я думаю это не вариант?

  • я попытался изменить заголовки с помощью инспектора в IDispatchMessage.Метод beforesendreply но в этом укажите Content-Length заголовок еще не удален, и Transfer-Encoding еще не установлен, поэтому "слишком рано". Позже я прочитал, что кодировка передачи фрагментов находится на уровне TCP, поэтому изменение заголовка в этот момент, вероятно, не будет работать, даже если бы я мог.

  • я попытался установить aspNetCompatibilityEnabled="true", установка режима передачи wcf в буферизованный вывод (по умолчанию), а затем установить System.Web.HttpContext.Current.Response.BufferOutput = false;. Это игнорируется wcf, хотя сообщение явно буферизованный.

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

2 ответов


это проверенный обходной путь, который работает только для WCF в IIS-я не нашел никакого решения для автономной службы.

короче-включить aspNetCompatibility что дает вам доступ во время выполнения к


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

пример @server:

public Stream GetBigData()
{
    byte[] bigData = GetBigDataFromSomeWhere();
    var ms = new MemoryStream();
    var lengthInfo = BitConverter.GetBytes(bigData.Length);
    ms.Write(lengthInfo, 0, lengthInfo.Length);
    ms.Write(bigData , 0, bigData .Length);
    ms.Flush();
    ms.Position = 0;
    return ms;
}

пример @client:

using (var respStream = yourService.GetBigData())
using (var resultStream = new MemoryStream())
{
    //read first 4 bytes 
    var lengthBuffer = new byte[4];
    respStream.Read(lengthBuffer, 0, 4);
    var totalCount = BitConverter.ToInt32(lengthBuffer, 0);
    resultStream.Capacity = totalCount;

    //read rest
    var readcount = 0;
    var buffer = new byte[1024 * 32];
    while ((readcount = respStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        resultStream.Write(buffer, 0, readcount);
        UpdateProgress(readcount, totalCount)
    }
}