Я создал простой сервис WCF для прототипа загрузки файлов. Услуга:
[ServiceContract]
public class Service1
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/Upload")]
public void Upload(Stream stream)
{
using (FileStream targetStream = new FileStream(@"C:\Test\output.txt", FileMode.Create, FileAccess.Write))
{
stream.CopyTo(targetStream);
}
}
}
В нем используется webHttpBinding
с transferMode
, установленным в "Streamed" и maxReceivedMessageSize
, maxBufferPoolSize
и maxBufferSize
, все установлены в 2GB. httpRuntime
имеет maxRequestLength
значение 10 МБ.
Клиент выдает HTTP-запросы следующим образом:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(@"http://.../Service1.svc/Upload");
request.Method = "POST";
request.SendChunked = true;
request.AllowWriteStreamBuffering = false;
request.ContentType = MediaTypeNames.Application.Octet;
using (FileStream inputStream = new FileStream(@"C:\input.txt", FileMode.Open, FileAccess.Read))
{
using (Stream outputStream = request.GetRequestStream())
{
inputStream.CopyTo(outputStream);
}
}
Теперь, наконец, что не так:
При загрузке файла размером 100 МБ сервер возвращает HTTP 400 (неверный запрос). Я попытался включить трассировку WCF, но не обнаружил ошибки. Когда я увеличиваю httpRuntime
. maxRequestLength
до 1 ГБ, файл загружается без проблем. MSDN говорит, что maxRequestLength
"задает ограничение для порога буферизации входного потока в КБ".
Это заставляет меня думать, что весь файл (все его 100 МБ) сначала сохраняется в "буфере входного потока", и только тогда он доступен для моего метода Upload
на сервере. Я действительно могу видеть, что размер файла на сервере не увеличивается постепенно (как и следовало ожидать), вместо этого, в момент его создания он уже имеет размер 100 МБ.
Вопрос: Как я могу заставить это работать, чтобы "буфер входного потока" был достаточно мал (скажем, 1 МБ), и когда он переполняется, мой метод Upload
вызывается? Другими словами, я хочу, чтобы загрузка была действительно потоковой, без необходимости хранить весь файл в любом месте.
EDIT:
Теперь я обнаружил, что httpRuntime
содержит другой параметр, который здесь имеет значение - requestLengthDiskThreshold
. Похоже, что когда входной буфер растет выше этого порога, он больше не сохраняется в памяти, а вместо этого в файловой системе. Так что, по крайней мере, весь большой файл размером 100 МБ не хранится в памяти (это то, чего я больше всего боялся), однако мне все же хотелось бы знать, есть ли способ избежать этого буфера вообще.