Передача больших файлов: объединение потоковой передачи и длины содержимого
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)
}
}