У меня есть файл журнала, в который непрерывно записывается фоновая служба.Пользователи должны быть в состоянии загрузить файл до сих пор.Когда я возвращаю MVC FileResult
, я получаю InvalidOperationException из-за несоответствия длины содержимого, предположительно потому, что некоторое содержимое было записано в файл во время его обслуживания.Файл обслуживается, и в большинстве случаев он в порядке, но обычно он имеет неполную последнюю строку.
Фоновая служба выполняет по существу следующее:
var stream = new FileStream(evidenceFilePath, FileMode.Append, FileAccess.Write, FileShare.Read);
while (true) // obviously it isn't actually this, but it does happen a lot!
{
var content = "log content\r\n";
stream.Write(Encoding.UTF8.GetBytes(content);
}
Вот некоторые вариантыдействие контроллера (все имеют одинаковый результат):
public IActionResult DownloadLog1()
{
return PhysicalFile("C:\\path\\to\\the\\file.txt", "text/plain", enableRangeProcessing: false); // also tried using true
}
public IActionResult DownloadLog2()
{
var stream = new FileStream("C:\\path\\to\\the\\file.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return File(stream, "text/plain", enableRangeProcessing: false); // also tried true
}
Вот исключение, которое я получаю, когда пытаюсь выполнить одно из указанных выше действий:
System.InvalidOperationException: Response Content-Length mismatch: too many bytes written (216072192 of 216059904).
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowTooManyBytesWritten(Int32 count)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.VerifyAndUpdateWrite(Int32 count)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.WriteAsync(ReadOnlyMemory`1 data, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.Extensions.StreamCopyOperation.CopyToAsync(Stream source, Stream destination, Nullable`1 count, Int32 bufferSize, CancellationToken cancel)
at Microsoft.AspNetCore.Mvc.Infrastructure.FileResultExecutorBase.WriteFileAsync(HttpContext context, Stream fileStream, RangeItemHeaderValue range, Int64 rangeLength)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync[TFilter,TFilterAsync]()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
Я тоже не против исключениямного, но я бы предпочел, чтобы этого не случилось.Мне нужно исправить неполную проблему с последней строкой.Самым очевидным решением для меня является отслеживание количества байтов, которые однозначно записаны в файл и каким-то образом обслуживают только эти первые n байтов.Я не вижу простого способа сделать это с помощью FileResult
и различных вспомогательных методов, которые его создают.Файл может быть довольно большим (до 500 МБ), поэтому не представляется целесообразным буферизовать в памяти.