| File: Infrastructure\FileStreamResultExecutor.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Mvc.Infrastructure; /// <summary> /// An <see cref="IActionResultExecutor{FileStreamResult}"/> for a file stream result. /// </summary> public partial class FileStreamResultExecutor : FileResultExecutorBase, IActionResultExecutor<FileStreamResult> { /// <summary> /// Initializes a new <see cref="FileStreamResultExecutor"/>. /// </summary> /// <param name="loggerFactory">The factory used to create loggers.</param> public FileStreamResultExecutor(ILoggerFactory loggerFactory) : base(CreateLogger<FileStreamResultExecutor>(loggerFactory)) { } /// <inheritdoc /> public virtual async Task ExecuteAsync(ActionContext context, FileStreamResult result) { ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(result); await using (result.FileStream) { Log.ExecutingFileResult(Logger, result); long? fileLength = null; if (result.FileStream.CanSeek) { fileLength = result.FileStream.Length; } var (range, rangeLength, serveBody) = SetHeadersAndLog( context, result, fileLength, result.EnableRangeProcessing, result.LastModified, result.EntityTag); if (!serveBody) { return; } await WriteFileAsync(context, result, range, rangeLength); } } /// <summary> /// Write the contents of the FileStreamResult to the response body. /// </summary> /// <param name="context">The <see cref="ActionContext"/>.</param> /// <param name="result">The FileStreamResult to write.</param> /// <param name="range">The <see cref="RangeItemHeaderValue"/>.</param> /// <param name="rangeLength">The range length.</param> protected virtual Task WriteFileAsync( ActionContext context, FileStreamResult result, RangeItemHeaderValue? range, long rangeLength) { ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(result); if (range != null && rangeLength == 0) { return Task.CompletedTask; } if (range != null) { Log.WritingRangeToBody(Logger); } return WriteFileAsync(context.HttpContext, result.FileStream, range, rangeLength); } private static partial class Log { public static void ExecutingFileResult(ILogger logger, FileResult fileResult) { if (logger.IsEnabled(LogLevel.Information)) { var fileResultType = fileResult.GetType().Name; ExecutingFileResultWithNoFileName(logger, fileResultType, fileResult.FileDownloadName); } } [LoggerMessage(1, LogLevel.Information, "Executing {FileResultType}, sending file with download name '{FileDownloadName}' ...", EventName = "ExecutingFileResultWithNoFileName", SkipEnabledCheck = true)] private static partial void ExecutingFileResultWithNoFileName(ILogger logger, string fileResultType, string fileDownloadName); [LoggerMessage(17, LogLevel.Debug, "Writing the requested range of bytes to the body...", EventName = "WritingRangeToBody")] public static partial void WritingRangeToBody(ILogger logger); } }