-
Notifications
You must be signed in to change notification settings - Fork 10k
/
FileStreamHttpResult.cs
139 lines (121 loc) · 5.16 KB
/
FileStreamHttpResult.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Http.HttpResults;
/// <summary>
/// Represents an <see cref="IResult"/> that when executed will
/// write a file from a stream to the response.
/// </summary>
public sealed class FileStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
{
/// <summary>
/// Creates a new <see cref="FileStreamHttpResult"/> instance with
/// the provided <paramref name="fileStream"/> and the
/// provided <paramref name="contentType"/>.
/// </summary>
/// <param name="fileStream">The stream with the file.</param>
/// <param name="contentType">The Content-Type of the file.</param>
internal FileStreamHttpResult(Stream fileStream, string? contentType)
: this(fileStream, contentType, fileDownloadName: null)
{
}
/// <summary>
/// Creates a new <see cref="FileStreamHttpResult"/> instance with
/// the provided <paramref name="fileStream"/>, the provided <paramref name="contentType"/>
/// and the provided <paramref name="fileDownloadName"/>.
/// </summary>
/// <param name="fileStream">The stream with the file.</param>
/// <param name="contentType">The Content-Type header of the response.</param>
/// <param name="fileDownloadName">The suggested file name.</param>
internal FileStreamHttpResult(
Stream fileStream,
string? contentType,
string? fileDownloadName)
: this(fileStream, contentType, fileDownloadName, enableRangeProcessing: false)
{
}
/// <summary>
/// Creates a new <see cref="FileStreamHttpResult"/> instance with the provided values.
/// </summary>
/// <param name="fileStream">The stream with the file.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="fileDownloadName">The suggested file name.</param>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
/// <param name="entityTag">The <see cref="EntityTagHeaderValue"/> associated with the file.</param>
internal FileStreamHttpResult(
Stream fileStream,
string? contentType,
string? fileDownloadName,
bool enableRangeProcessing,
DateTimeOffset? lastModified = null,
EntityTagHeaderValue? entityTag = null)
{
ArgumentNullException.ThrowIfNull(fileStream);
FileStream = fileStream;
if (fileStream.CanSeek)
{
FileLength = fileStream.Length;
}
ContentType = contentType ?? "application/octet-stream";
FileDownloadName = fileDownloadName;
EnableRangeProcessing = enableRangeProcessing;
LastModified = lastModified;
EntityTag = entityTag;
}
/// <summary>
/// Gets the Content-Type header for the response.
/// </summary>
public string ContentType { get; internal set; }
/// <summary>
/// Gets the file name that will be used in the Content-Disposition header of the response.
/// </summary>
public string? FileDownloadName { get; internal set; }
/// <summary>
/// Gets the last modified information associated with the file result.
/// </summary>
public DateTimeOffset? LastModified { get; internal set; }
/// <summary>
/// Gets the etag associated with the file result.
/// </summary>
public EntityTagHeaderValue? EntityTag { get; internal init; }
/// <summary>
/// Gets the value that enables range processing for the file result.
/// </summary>
public bool EnableRangeProcessing { get; internal init; }
/// <summary>
/// Gets or sets the file length information .
/// </summary>
public long? FileLength { get; internal set; }
/// <summary>
/// Gets the stream with the file that will be sent back as the response.
/// </summary>
public Stream FileStream { get; }
/// <inheritdoc/>
public async Task ExecuteAsync(HttpContext httpContext)
{
ArgumentNullException.ThrowIfNull(httpContext);
// Creating the logger with a string to preserve the category after the refactoring.
var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.FileStreamResult");
await using (FileStream)
{
var (range, rangeLength, completed) = HttpResultsHelper.WriteResultAsFileCore(
httpContext,
logger,
FileDownloadName,
FileLength,
ContentType,
EnableRangeProcessing,
LastModified,
EntityTag);
if (!completed)
{
await FileResultHelper.WriteFileAsync(httpContext, FileStream, range, rangeLength);
}
}
}
}