Add EnableRangeProcessing (#6895)

Addresses #6780
This commit is contained in:
Jass Bagga 2017-09-29 18:39:45 -07:00 committed by GitHub
parent eeac99985a
commit 2fcfc6be7c
16 changed files with 816 additions and 179 deletions

View File

@ -1088,6 +1088,20 @@ namespace Microsoft.AspNetCore.Mvc
public virtual FileContentResult File(byte[] fileContents, string contentType)
=> File(fileContents, contentType, fileDownloadName: null);
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>),
/// and the specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileContents">The file contents.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="FileContentResult"/> for the response.</returns>
[NonAction]
public virtual FileContentResult File(byte[] fileContents, string contentType, bool enableRangeProcessing)
=> File(fileContents, contentType, fileDownloadName: null, enableRangeProcessing: enableRangeProcessing);
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type and the specified <paramref name="fileDownloadName" /> as the suggested file name.
@ -1102,6 +1116,25 @@ namespace Microsoft.AspNetCore.Mvc
public virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName)
=> new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName };
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type and the specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileContents">The file contents.</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>
/// <returns>The created <see cref="FileContentResult"/> for the response.</returns>
[NonAction]
public virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName, bool enableRangeProcessing)
=> new FileContentResult(fileContents, contentType)
{
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>),
/// and the specified <paramref name="contentType" /> as the Content-Type.
@ -1123,6 +1156,29 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>),
/// and the specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileContents">The file contents.</param>
/// <param name="contentType">The Content-Type of the file.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="FileContentResult"/> for the response.</returns>
[NonAction]
public virtual FileContentResult File(byte[] fileContents, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new FileContentResult(fileContents, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
@ -1146,6 +1202,31 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns a file with the specified <paramref name="fileContents" /> as content (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileContents">The file contents.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="fileDownloadName">The suggested file name.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="FileContentResult"/> for the response.</returns>
[NonAction]
public virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new FileContentResult(fileContents, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>), with the
/// specified <paramref name="contentType" /> as the Content-Type.
@ -1159,6 +1240,20 @@ namespace Microsoft.AspNetCore.Mvc
public virtual FileStreamResult File(Stream fileStream, string contentType)
=> File(fileStream, contentType, fileDownloadName: null);
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>), with the
/// specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileStream">The <see cref="Stream"/> with the contents of the file.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="FileStreamResult"/> for the response.</returns>
[NonAction]
public virtual FileStreamResult File(Stream fileStream, string contentType, bool enableRangeProcessing)
=> File(fileStream, contentType, fileDownloadName: null, enableRangeProcessing: enableRangeProcessing);
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type and the
@ -1174,6 +1269,26 @@ namespace Microsoft.AspNetCore.Mvc
public virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName)
=> new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName };
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type and the
/// specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileStream">The <see cref="Stream"/> with the contents of 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>
/// <returns>The created <see cref="FileStreamResult"/> for the response.</returns>
[NonAction]
public virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName, bool enableRangeProcessing)
=> new FileStreamResult(fileStream, contentType)
{
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>),
/// and the specified <paramref name="contentType" /> as the Content-Type.
@ -1195,6 +1310,29 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>),
/// and the specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileStream">The <see cref="Stream"/> with the contents of the file.</param>
/// <param name="contentType">The Content-Type of the file.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="FileStreamResult"/> for the response.</returns>
[NonAction]
public virtual FileStreamResult File(Stream fileStream, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new FileStreamResult(fileStream, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
@ -1218,6 +1356,31 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns a file in the specified <paramref name="fileStream" /> (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="fileStream">The <see cref="Stream"/> with the contents of the file.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="fileDownloadName">The suggested file name.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="FileStreamResult"/> for the response.</returns>
[NonAction]
public virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new FileStreamResult(fileStream, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type.
@ -1231,6 +1394,20 @@ namespace Microsoft.AspNetCore.Mvc
public virtual VirtualFileResult File(string virtualPath, string contentType)
=> File(virtualPath, contentType, fileDownloadName: null);
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="virtualPath">The virtual path of the file to be returned.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="VirtualFileResult"/> for the response.</returns>
[NonAction]
public virtual VirtualFileResult File(string virtualPath, string contentType, bool enableRangeProcessing)
=> File(virtualPath, contentType, fileDownloadName: null, enableRangeProcessing: enableRangeProcessing);
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type and the
@ -1246,6 +1423,26 @@ namespace Microsoft.AspNetCore.Mvc
public virtual VirtualFileResult File(string virtualPath, string contentType, string fileDownloadName)
=> new VirtualFileResult(virtualPath, contentType) { FileDownloadName = fileDownloadName };
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type and the
/// specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="virtualPath">The virtual path of the file to be returned.</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>
/// <returns>The created <see cref="VirtualFileResult"/> for the response.</returns>
[NonAction]
public virtual VirtualFileResult File(string virtualPath, string contentType, string fileDownloadName, bool enableRangeProcessing)
=> new VirtualFileResult(virtualPath, contentType)
{
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>), and the
/// specified <paramref name="contentType" /> as the Content-Type.
@ -1267,6 +1464,29 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>), and the
/// specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="virtualPath">The virtual path of the file to be returned.</param>
/// <param name="contentType">The Content-Type of the file.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="VirtualFileResult"/> for the response.</returns>
[NonAction]
public virtual VirtualFileResult File(string virtualPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new VirtualFileResult(virtualPath, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
@ -1290,6 +1510,31 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns the file specified by <paramref name="virtualPath" /> (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="virtualPath">The virtual path of the file to be returned.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="fileDownloadName">The suggested file name.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="VirtualFileResult"/> for the response.</returns>
[NonAction]
public virtual VirtualFileResult File(string virtualPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new VirtualFileResult(virtualPath, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type.
@ -1303,6 +1548,20 @@ namespace Microsoft.AspNetCore.Mvc
public virtual PhysicalFileResult PhysicalFile(string physicalPath, string contentType)
=> PhysicalFile(physicalPath, contentType, fileDownloadName: null);
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="physicalPath">The physical path of the file to be returned.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
[NonAction]
public virtual PhysicalFileResult PhysicalFile(string physicalPath, string contentType, bool enableRangeProcessing)
=> PhysicalFile(physicalPath, contentType, fileDownloadName: null, enableRangeProcessing: enableRangeProcessing);
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type and the
@ -1321,6 +1580,30 @@ namespace Microsoft.AspNetCore.Mvc
string fileDownloadName)
=> new PhysicalFileResult(physicalPath, contentType) { FileDownloadName = fileDownloadName };
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
/// specified <paramref name="contentType" /> as the Content-Type and the
/// specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="physicalPath">The physical path of the file to be returned.</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>
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
[NonAction]
public virtual PhysicalFileResult PhysicalFile(
string physicalPath,
string contentType,
string fileDownloadName,
bool enableRangeProcessing)
=> new PhysicalFileResult(physicalPath, contentType)
{
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>), and
/// the specified <paramref name="contentType" /> as the Content-Type.
@ -1342,6 +1625,29 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>), and
/// the specified <paramref name="contentType" /> as the Content-Type.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="physicalPath">The physical path of the file to be returned.</param>
/// <param name="contentType">The Content-Type of the file.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
[NonAction]
public virtual PhysicalFileResult PhysicalFile(string physicalPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new PhysicalFileResult(physicalPath, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
@ -1365,6 +1671,31 @@ namespace Microsoft.AspNetCore.Mvc
};
}
/// <summary>
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>), the
/// specified <paramref name="contentType" /> as the Content-Type, and the specified <paramref name="fileDownloadName" /> as the suggested file name.
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
/// </summary>
/// <param name="physicalPath">The physical path of the file to be returned.</param>
/// <param name="contentType">The Content-Type of the file.</param>
/// <param name="fileDownloadName">The suggested file name.</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>
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
[NonAction]
public virtual PhysicalFileResult PhysicalFile(string physicalPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing)
{
return new PhysicalFileResult(physicalPath, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
FileDownloadName = fileDownloadName,
EnableRangeProcessing = enableRangeProcessing,
};
}
/// <summary>
/// Creates an <see cref="UnauthorizedResult"/> that produces an <see cref="StatusCodes.Status401Unauthorized"/> response.
/// </summary>

View File

@ -52,5 +52,10 @@ namespace Microsoft.AspNetCore.Mvc
/// Gets or sets the etag associated with the <see cref="FileResult"/>.
/// </summary>
public EntityTagHeaderValue EntityTag { get; set; }
/// <summary>
/// Gets or sets the value that enables range processing for the <see cref="FileResult"/>.
/// </summary>
public bool EnableRangeProcessing { get; set; }
}
}

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
context,
result,
result.FileContents.Length,
result.EnableRangeProcessing,
result.LastModified,
result.EntityTag);

View File

@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
ActionContext context,
FileResult result,
long? fileLength,
bool enableRangeProcessing,
DateTimeOffset? lastModified = null,
EntityTagHeaderValue etag = null)
{
@ -59,54 +60,55 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var request = context.HttpContext.Request;
var httpRequestHeaders = request.GetTypedHeaders();
var preconditionState = GetPreconditionState(httpRequestHeaders, lastModified, etag);
var response = context.HttpContext.Response;
var httpResponseHeaders = response.GetTypedHeaders();
if (lastModified.HasValue)
{
httpResponseHeaders.LastModified = lastModified;
}
if (etag != null)
{
httpResponseHeaders.ETag = etag;
}
SetLastModifiedAndEtagHeaders(response, lastModified, etag);
var serveBody = !HttpMethods.IsHead(request.Method);
var preconditionState = GetPreconditionState(context, httpRequestHeaders, lastModified, etag);
// Short circuit if the preconditional headers process to 304 (NotModified) or 412 (PreconditionFailed)
if (preconditionState == PreconditionState.NotModified)
{
serveBody = false;
response.StatusCode = StatusCodes.Status304NotModified;
return (range: null, rangeLength: 0, serveBody);
}
else if (preconditionState == PreconditionState.PreconditionFailed)
{
serveBody = false;
response.StatusCode = StatusCodes.Status412PreconditionFailed;
return (range: null, rangeLength: 0, serveBody);
}
if (fileLength.HasValue)
{
SetAcceptRangeHeader(context);
// Assuming the request is not a range request, the Content-Length header is set to the length of the entire file.
// Assuming the request is not a range request, and the response body is not empty, the Content-Length header is set to
// the length of the entire file.
// If the request is a valid range request, this header is overwritten with the length of the range as part of the
// range processing (see method SetContentLength).
if (serveBody)
{
response.ContentLength = fileLength.Value;
}
if (HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
// Handle range request
if (enableRangeProcessing)
{
if ((preconditionState == PreconditionState.Unspecified ||
preconditionState == PreconditionState.ShouldProcess))
SetAcceptRangeHeader(response);
// If the request method is HEAD or GET, PreconditionState is Unspecified or ShouldProcess, and IfRange header is valid,
// range should be processed and Range headers should be set
if ((HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
&& (preconditionState == PreconditionState.Unspecified || preconditionState == PreconditionState.ShouldProcess)
&& (IfRangeValid(httpRequestHeaders, lastModified, etag)))
{
if (IfRangeValid(context, httpRequestHeaders, lastModified, etag))
{
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
}
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
}
}
}
return (range: null, rangeLength: 0, serveBody: serveBody);
return (range: null, rangeLength: 0, serveBody);
}
private static void SetContentType(ActionContext context, FileResult result)
@ -130,38 +132,25 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
}
}
private static void SetAcceptRangeHeader(ActionContext context)
private static void SetLastModifiedAndEtagHeaders(HttpResponse response, DateTimeOffset? lastModified, EntityTagHeaderValue etag)
{
var httpResponseHeaders = response.GetTypedHeaders();
if (lastModified.HasValue)
{
httpResponseHeaders.LastModified = lastModified;
}
if (etag != null)
{
httpResponseHeaders.ETag = etag;
}
}
private static void SetAcceptRangeHeader(HttpResponse response)
{
var response = context.HttpContext.Response;
response.Headers[HeaderNames.AcceptRanges] = AcceptRangeHeaderValue;
}
private static PreconditionState GetEtagMatchState(
IList<EntityTagHeaderValue> etagHeader,
EntityTagHeaderValue etag,
PreconditionState matchFoundState,
PreconditionState matchNotFoundState)
{
if (etagHeader != null && etagHeader.Any())
{
var state = matchNotFoundState;
foreach (var entityTag in etagHeader)
{
if (entityTag.Equals(EntityTagHeaderValue.Any) || entityTag.Compare(etag, useStrongComparison: true))
{
state = matchFoundState;
break;
}
}
return state;
}
return PreconditionState.Unspecified;
}
internal static bool IfRangeValid(
ActionContext context,
RequestHeaders httpRequestHeaders,
DateTimeOffset? lastModified = null,
EntityTagHeaderValue etag = null)
@ -193,7 +182,6 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
// Internal for testing
internal static PreconditionState GetPreconditionState(
ActionContext context,
RequestHeaders httpRequestHeaders,
DateTimeOffset? lastModified = null,
EntityTagHeaderValue etag = null)
@ -247,6 +235,30 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return state;
}
private static PreconditionState GetEtagMatchState(
IList<EntityTagHeaderValue> etagHeader,
EntityTagHeaderValue etag,
PreconditionState matchFoundState,
PreconditionState matchNotFoundState)
{
if (etagHeader != null && etagHeader.Any())
{
var state = matchNotFoundState;
foreach (var entityTag in etagHeader)
{
if (entityTag.Equals(EntityTagHeaderValue.Any) || entityTag.Compare(etag, useStrongComparison: true))
{
state = matchFoundState;
break;
}
}
return state;
}
return PreconditionState.Unspecified;
}
private static PreconditionState GetMaxPreconditionState(params PreconditionState[] states)
{
var max = PreconditionState.Unspecified;
@ -281,6 +293,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return (range: null, rangeLength: 0, serveBody: true);
}
// Requested range is not satisfiable
if (range == null)
{
// 14.16 Content-Range - A server sending a response with status code 416 (Requested range not satisfiable)
@ -292,23 +305,23 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return (range: null, rangeLength: 0, serveBody: false);
}
response.StatusCode = StatusCodes.Status206PartialContent;
httpResponseHeaders.ContentRange = new ContentRangeHeaderValue(
range.From.Value,
range.To.Value,
fileLength);
response.StatusCode = StatusCodes.Status206PartialContent;
// Overwrite the Content-Length header for valid range requests with the range length.
var rangeLength = SetContentLength(context, range);
var rangeLength = SetContentLength(response, range);
return (range, rangeLength, serveBody: true);
}
private static long SetContentLength(ActionContext context, RangeItemHeaderValue range)
private static long SetContentLength(HttpResponse response, RangeItemHeaderValue range)
{
var start = range.From.Value;
var end = range.To.Value;
var length = end - start + 1;
var response = context.HttpContext.Response;
response.ContentLength = length;
return length;
}

View File

@ -38,6 +38,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
context,
result,
fileLength,
result.EnableRangeProcessing,
result.LastModified,
result.EntityTag);

View File

@ -44,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
context,
result,
fileInfo.Length,
result.EnableRangeProcessing,
lastModified,
result.EntityTag);

View File

@ -14,7 +14,7 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
public class VirtualFileResultExecutor : FileResultExecutorBase , IActionResultExecutor<VirtualFileResult>
public class VirtualFileResultExecutor : FileResultExecutorBase, IActionResultExecutor<VirtualFileResult>
{
private readonly IHostingEnvironment _hostingEnvironment;
@ -54,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
context,
result,
fileInfo.Length,
result.EnableRangeProcessing,
lastModified,
result.EntityTag);

View File

@ -1632,14 +1632,33 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Same(fileContents, result.FileContents);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.False(result.EnableRangeProcessing);
}
[Fact]
public void File_WithContents_EnableRangeProcessing()
{
// Arrange
var controller = new TestableController();
var fileContents = new byte[0];
// Act
var result = controller.File(fileContents, "application/pdf", true);
// Assert
Assert.NotNull(result);
Assert.Same(fileContents, result.FileContents);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.True(result.EnableRangeProcessing);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "\"Etag\"")]
[InlineData("05/01/2008 +1:00", null)]
[InlineData("05/01/2008 +1:00", "\"Etag\"")]
public void File_WithContents_LastModifiedAndEtag(string lastModifiedString, string entityTagString)
[InlineData(null, null, false)]
[InlineData(null, "\"Etag\"", false)]
[InlineData("05/01/2008 +1:00", null, true)]
[InlineData("05/01/2008 +1:00", "\"Etag\"", true)]
public void File_WithContents_LastModifiedAndEtag(string lastModifiedString, string entityTagString, bool enableRangeProcessing)
{
// Arrange
var controller = new TestableController();
@ -1648,7 +1667,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var entityTag = (entityTagString == null) ? null : new EntityTagHeaderValue(entityTagString);
// Act
var result = controller.File(fileContents, "application/pdf", lastModified, entityTag);
var result = controller.File(fileContents, "application/pdf", lastModified, entityTag, enableRangeProcessing);
// Assert
Assert.NotNull(result);
@ -1657,6 +1676,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.Equal(lastModified, result.LastModified);
Assert.Equal(entityTag, result.EntityTag);
Assert.Equal(enableRangeProcessing, result.EnableRangeProcessing);
}
[Fact]
@ -1674,14 +1694,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Same(fileContents, result.FileContents);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal("someDownloadName", result.FileDownloadName);
Assert.False(result.EnableRangeProcessing);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "\"Etag\"")]
[InlineData("05/01/2008 +1:00", null)]
[InlineData("05/01/2008 +1:00", "\"Etag\"")]
public void File_WithContentsAndFileDownloadName_LastModifiedAndEtag(string lastModifiedString, string entityTagString)
[InlineData(null, null, false)]
[InlineData(null, "\"Etag\"", false)]
[InlineData("05/01/2008 +1:00", null, true)]
[InlineData("05/01/2008 +1:00", "\"Etag\"", true)]
public void File_WithContentsAndFileDownloadName_LastModifiedAndEtag(string lastModifiedString, string entityTagString, bool enableRangeProcessing)
{
// Arrange
var controller = new TestableController();
@ -1690,7 +1711,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var entityTag = (entityTagString == null) ? null : new EntityTagHeaderValue(entityTagString);
// Act
var result = controller.File(fileContents, "application/pdf", "someDownloadName", lastModified, entityTag);
var result = controller.File(fileContents, "application/pdf", "someDownloadName", lastModified, entityTag, enableRangeProcessing);
// Assert
Assert.NotNull(result);
@ -1699,6 +1720,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal("someDownloadName", result.FileDownloadName);
Assert.Equal(lastModified, result.LastModified);
Assert.Equal(entityTag, result.EntityTag);
Assert.Equal(enableRangeProcessing, result.EnableRangeProcessing);
}
[Fact]
@ -1716,14 +1738,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal(path, result.FileName);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.False(result.EnableRangeProcessing);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "\"Etag\"")]
[InlineData("05/01/2008 +1:00", null)]
[InlineData("05/01/2008 +1:00", "\"Etag\"")]
public void File_WithPath_LastModifiedAndEtag(string lastModifiedString, string entityTagString)
[InlineData(null, null, false)]
[InlineData(null, "\"Etag\"", false)]
[InlineData("05/01/2008 +1:00", null, true)]
[InlineData("05/01/2008 +1:00", "\"Etag\"", true)]
public void File_WithPath_LastModifiedAndEtag(string lastModifiedString, string entityTagString, bool enableRangeProcessing)
{
// Arrange
var controller = new TestableController();
@ -1732,7 +1755,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var entityTag = (entityTagString == null) ? null : new EntityTagHeaderValue(entityTagString);
// Act
var result = controller.File(path, "application/pdf", lastModified, entityTag);
var result = controller.File(path, "application/pdf", lastModified, entityTag, enableRangeProcessing);
// Assert
Assert.NotNull(result);
@ -1741,6 +1764,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.Equal(lastModified, result.LastModified);
Assert.Equal(entityTag, result.EntityTag);
Assert.Equal(enableRangeProcessing, result.EnableRangeProcessing);
}
[Fact]
@ -1758,14 +1782,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal(path, result.FileName);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal("someDownloadName", result.FileDownloadName);
Assert.False(result.EnableRangeProcessing);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "\"Etag\"")]
[InlineData("05/01/2008 +1:00", null)]
[InlineData("05/01/2008 +1:00", "\"Etag\"")]
public void File_WithPathAndFileDownloadName_LastModifiedAndEtag(string lastModifiedString, string entityTagString)
[InlineData(null, null, false)]
[InlineData(null, "\"Etag\"", false)]
[InlineData("05/01/2008 +1:00", null, true)]
[InlineData("05/01/2008 +1:00", "\"Etag\"", true)]
public void File_WithPathAndFileDownloadName_LastModifiedAndEtag(string lastModifiedString, string entityTagString, bool enableRangeProcessing)
{
// Arrange
var controller = new TestableController();
@ -1774,7 +1799,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var entityTag = (entityTagString == null) ? null : new EntityTagHeaderValue(entityTagString);
// Act
var result = controller.File(path, "application/pdf", "someDownloadName", lastModified, entityTag);
var result = controller.File(path, "application/pdf", "someDownloadName", lastModified, entityTag, enableRangeProcessing);
// Assert
Assert.NotNull(result);
@ -1783,6 +1808,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal("someDownloadName", result.FileDownloadName);
Assert.Equal(lastModified, result.LastModified);
Assert.Equal(entityTag, result.EntityTag);
Assert.Equal(enableRangeProcessing, result.EnableRangeProcessing);
}
[Fact]
@ -1805,14 +1831,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Same(fileStream, result.FileStream);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.False(result.EnableRangeProcessing);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "\"Etag\"")]
[InlineData("05/01/2008 +1:00", null)]
[InlineData("05/01/2008 +1:00", "\"Etag\"")]
public void File_WithStream_LastModifiedAndEtag(string lastModifiedString, string entityTagString)
[InlineData(null, null, false)]
[InlineData(null, "\"Etag\"", false)]
[InlineData("05/01/2008 +1:00", null, true)]
[InlineData("05/01/2008 +1:00", "\"Etag\"", true)]
public void File_WithStream_LastModifiedAndEtag(string lastModifiedString, string entityTagString, bool enableRangeProcessing)
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
@ -1826,7 +1853,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var entityTag = (entityTagString == null) ? null : new EntityTagHeaderValue(entityTagString);
// Act
var result = controller.File(fileStream, "application/pdf", lastModified, entityTag);
var result = controller.File(fileStream, "application/pdf", lastModified, entityTag, enableRangeProcessing);
// Assert
Assert.NotNull(result);
@ -1835,6 +1862,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal(string.Empty, result.FileDownloadName);
Assert.Equal(lastModified, result.LastModified);
Assert.Equal(entityTag, result.EntityTag);
Assert.Equal(enableRangeProcessing, result.EnableRangeProcessing);
}
[Fact]
@ -1856,14 +1884,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Same(fileStream, result.FileStream);
Assert.Equal("application/pdf", result.ContentType.ToString());
Assert.Equal("someDownloadName", result.FileDownloadName);
Assert.False(result.EnableRangeProcessing);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "\"Etag\"")]
[InlineData("05/01/2008 +1:00", null)]
[InlineData("05/01/2008 +1:00", "\"Etag\"")]
public void File_WithStreamAndFileDownloadName_LastModifiedAndEtag(string lastModifiedString, string entityTagString)
[InlineData(null, null, false)]
[InlineData(null, "\"Etag\"", false)]
[InlineData("05/01/2008 +1:00", null, true)]
[InlineData("05/01/2008 +1:00", "\"Etag\"", true)]
public void File_WithStreamAndFileDownloadName_LastModifiedAndEtag(string lastModifiedString, string entityTagString, bool enableRangeProcessing)
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
@ -1876,7 +1905,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var entityTag = (entityTagString == null) ? null : new EntityTagHeaderValue(entityTagString);
// Act
var result = controller.File(fileStream, "application/pdf", "someDownloadName", lastModified, entityTag);
var result = controller.File(fileStream, "application/pdf", "someDownloadName", lastModified, entityTag, enableRangeProcessing);
// Assert
Assert.NotNull(result);
@ -1885,6 +1914,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
Assert.Equal("someDownloadName", result.FileDownloadName);
Assert.Equal(lastModified, result.LastModified);
Assert.Equal(entityTag, result.EntityTag);
Assert.Equal(enableRangeProcessing, result.EnableRangeProcessing);
}
[Fact]
@ -2738,7 +2768,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var controller = new TestableController();
// Act
var result = controller.RedirectToPage("page", "handler", new { test = "value"});
var result = controller.RedirectToPage("page", "handler", new { test = "value" });
// Assert
Assert.Equal("page", result.PageName);

View File

@ -8,7 +8,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -111,7 +110,8 @@ namespace Microsoft.AspNetCore.Mvc
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -135,18 +135,18 @@ namespace Microsoft.AspNetCore.Mvc
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
var contentRange = new ContentRangeHeaderValue(start.Value, end.Value, byteArray.Length);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(contentLength, httpResponse.ContentLength);
Assert.Equal(expectedString, body);
}
[Fact]
public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
public async Task WriteFileAsync_IfRangeHeaderValid_WritesRangeRequest()
{
// Arrange
var contentType = "text/plain";
@ -157,7 +157,8 @@ namespace Microsoft.AspNetCore.Mvc
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -180,18 +181,28 @@ namespace Microsoft.AspNetCore.Mvc
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(5, httpResponse.ContentLength);
Assert.Equal("Hello", body);
if (result.EnableRangeProcessing)
{
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(5, httpResponse.ContentLength);
Assert.Equal("Hello", body);
}
else
{
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal(11, httpResponse.ContentLength);
Assert.Equal("Hello World", body);
}
}
[Fact]
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedIgnored()
public async Task WriteFileAsync_RangeProcessingNotEnabled_RangeRequestIgnored()
{
// Arrange
var contentType = "text/plain";
@ -205,6 +216,48 @@ namespace Microsoft.AspNetCore.Mvc
EntityTag = entityTag
};
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfMatch = new[]
{
new EntityTagHeaderValue("\"Etag\""),
};
requestHeaders.Range = new RangeHeaderValue(0, 4);
requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
httpContext.Request.Method = HttpMethods.Get;
httpContext.Response.Body = new MemoryStream();
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
var httpResponse = actionContext.HttpContext.Response;
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("Hello World", body);
}
[Fact]
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestIgnored()
{
// Arrange
var contentType = "text/plain";
var lastModified = DateTimeOffset.MinValue.AddDays(1);
var entityTag = new EntityTagHeaderValue("\"Etag\"");
var byteArray = Encoding.ASCII.GetBytes("Hello World");
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfMatch = new[]
@ -226,7 +279,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("Hello World", body);
@ -247,7 +299,8 @@ namespace Microsoft.AspNetCore.Mvc
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -266,7 +319,6 @@ namespace Microsoft.AspNetCore.Mvc
var body = streamReader.ReadToEndAsync().Result;
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("Hello World", body);
@ -286,7 +338,8 @@ namespace Microsoft.AspNetCore.Mvc
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -304,16 +357,16 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
var contentRange = new ContentRangeHeaderValue(byteArray.Length);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Empty(body);
}
[Fact]
public async Task WriteFileAsync_RangeRequested_PreconditionFailed()
public async Task WriteFileAsync_PreconditionFailed_RangeRequestedIgnored()
{
// Arrange
var contentType = "text/plain";
@ -324,7 +377,8 @@ namespace Microsoft.AspNetCore.Mvc
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -347,7 +401,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status412PreconditionFailed, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -355,7 +408,7 @@ namespace Microsoft.AspNetCore.Mvc
}
[Fact]
public async Task WriteFileAsync_RangeRequested_NotModified()
public async Task WriteFileAsync_NotModified_RangeRequestedIgnored()
{
// Arrange
var contentType = "text/plain";
@ -366,7 +419,8 @@ namespace Microsoft.AspNetCore.Mvc
var result = new FileContentResult(byteArray, contentType)
{
LastModified = lastModified,
EntityTag = entityTag
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -389,7 +443,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status304NotModified, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);

View File

@ -297,7 +297,6 @@ namespace Microsoft.AspNetCore.Mvc
// Act
var state = FileResultExecutorBase.GetPreconditionState(
actionContext,
httpRequestHeaders,
lastModified,
etag);
@ -334,7 +333,6 @@ namespace Microsoft.AspNetCore.Mvc
// Act
var state = FileResultExecutorBase.GetPreconditionState(
actionContext,
httpRequestHeaders,
lastModified,
etag);
@ -370,7 +368,6 @@ namespace Microsoft.AspNetCore.Mvc
// Act
var state = FileResultExecutorBase.GetPreconditionState(
actionContext,
httpRequestHeaders,
lastModified,
etag);
@ -398,7 +395,6 @@ namespace Microsoft.AspNetCore.Mvc
// Act
var ifRangeIsValid = FileResultExecutorBase.IfRangeValid(
actionContext,
httpRequestHeaders,
lastModified,
etag);
@ -460,7 +456,7 @@ namespace Microsoft.AspNetCore.Mvc
public Task ExecuteAsync(ActionContext context, EmptyFileResult result)
{
SetHeadersAndLog(context, result, 0L);
SetHeadersAndLog(context, result, 0L, true);
result.WasWriteFileCalled = true;
return Task.FromResult(0);
}

View File

@ -10,7 +10,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -95,6 +94,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -119,11 +119,11 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
var contentRange = new ContentRangeHeaderValue(start.Value, end.Value, byteArray.Length);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(contentLength, httpResponse.ContentLength);
Assert.Equal(expectedString, body);
}
@ -143,6 +143,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -165,16 +166,59 @@ namespace Microsoft.AspNetCore.Mvc
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(5, httpResponse.ContentLength);
Assert.Equal("Hello", body);
}
[Fact]
public async Task WriteFileAsync_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var contentType = "text/plain";
var lastModified = DateTimeOffset.MinValue;
var entityTag = new EntityTagHeaderValue("\"Etag\"");
var byteArray = Encoding.ASCII.GetBytes("Hello World");
var readStream = new MemoryStream(byteArray);
readStream.SetLength(11);
var result = new FileStreamResult(readStream, contentType)
{
LastModified = lastModified,
EntityTag = entityTag,
};
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfMatch = new[]
{
new EntityTagHeaderValue("\"Etag\""),
};
requestHeaders.Range = new RangeHeaderValue(0, 4);
requestHeaders.IfRange = new RangeConditionHeaderValue(DateTimeOffset.MinValue);
httpContext.Request.Method = HttpMethods.Get;
httpContext.Response.Body = new MemoryStream();
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
var httpResponse = actionContext.HttpContext.Response;
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("Hello World", body);
}
[Fact]
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedIgnored()
{
@ -190,6 +234,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -235,6 +280,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -254,7 +300,6 @@ namespace Microsoft.AspNetCore.Mvc
var body = streamReader.ReadToEndAsync().Result;
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("Hello World", body);
@ -276,6 +321,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -293,11 +339,12 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
var contentRange = new ContentRangeHeaderValue(byteArray.Length);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal(11, httpResponse.ContentLength);
Assert.Empty(body);
}
@ -315,6 +362,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -337,7 +385,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status412PreconditionFailed, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -345,7 +392,7 @@ namespace Microsoft.AspNetCore.Mvc
}
[Fact]
public async Task WriteFileAsync_RangeRequested_NotModified()
public async Task WriteFileAsync_NotModified_RangeRequestedIgnored()
{
// Arrange
var contentType = "text/plain";
@ -358,6 +405,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -380,7 +428,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status304NotModified, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -404,6 +451,7 @@ namespace Microsoft.AspNetCore.Mvc
{
LastModified = lastModified,
EntityTag = entityTag,
EnableRangeProcessing = true,
};
var httpContext = GetHttpContext();
@ -425,13 +473,12 @@ namespace Microsoft.AspNetCore.Mvc
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
var contentRange = new ContentRangeHeaderValue(byteArray.Length);
Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Empty(body);
}

View File

@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -63,6 +62,7 @@ namespace Microsoft.AspNetCore.Mvc
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
result.EnableRangeProcessing = true;
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
@ -97,6 +97,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
result.EnableRangeProcessing = true;
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
@ -124,12 +125,42 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal("File", body);
}
[Fact]
public async Task WriteFileAsync_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
requestHeaders.Range = new RangeHeaderValue(0, 3);
requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
httpContext.Request.Method = HttpMethods.Get;
httpContext.Response.Body = new MemoryStream();
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
var httpResponse = actionContext.HttpContext.Response;
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal("FilePathResultTestFile contents<74>", body);
}
[Fact]
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedIgnored()
{
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
result.EnableRangeProcessing = true;
var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
@ -149,7 +180,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal("FilePathResultTestFile contents<74>", body);
}
@ -158,7 +188,7 @@ namespace Microsoft.AspNetCore.Mvc
[InlineData("0-5")]
[InlineData("bytes = ")]
[InlineData("bytes = 1-4, 5-11")]
public async Task WriteFileAsync_RangeRequestIgnored(string rangeString)
public async Task WriteFileAsync_RangeHeaderMalformed_RangeRequestIgnored(string rangeString)
{
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
@ -180,7 +210,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal("FilePathResultTestFile contents<74>", body);
@ -194,6 +223,7 @@ namespace Microsoft.AspNetCore.Mvc
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
result.EnableRangeProcessing = true;
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
@ -224,6 +254,7 @@ namespace Microsoft.AspNetCore.Mvc
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
result.EnableRangeProcessing = true;
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue;
@ -241,7 +272,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status412PreconditionFailed, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -254,6 +284,7 @@ namespace Microsoft.AspNetCore.Mvc
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
result.EnableRangeProcessing = true;
var httpContext = GetHttpContext();
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(1);
@ -271,7 +302,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status304NotModified, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -321,16 +351,16 @@ namespace Microsoft.AspNetCore.Mvc
}
[Theory]
[InlineData(0, 3, "File", 4)]
[InlineData(8, 13, "Result", 6)]
[InlineData(null, 3, "ts¡", 3)]
[InlineData(8, null, "ResultTestFile contents¡", 26)]
public async Task ExecuteResultAsync_CallsSendFileAsyncWithRequestedRange_IfIHttpSendFilePresent(long? start, long? end, string expectedString, long contentLength)
[InlineData(0, 3, 4)]
[InlineData(8, 13, 6)]
[InlineData(null, 3, 3)]
[InlineData(8, null, 26)]
public async Task ExecuteResultAsync_CallsSendFileAsyncWithRequestedRange_IfIHttpSendFilePresent(long? start, long? end, long contentLength)
{
// Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var result = new TestPhysicalFileResult(path, "text/plain");
result.EnableRangeProcessing = true;
var sendFile = new TestSendFileFeature();
var httpContext = GetHttpContext();
httpContext.Features.Set<IHttpSendFileFeature>(sendFile);
@ -351,7 +381,7 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal(Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt")), sendFile.Name);
Assert.Equal(start, sendFile.Offset);
Assert.Equal(contentLength, sendFile.Length);
Assert.Equal(CancellationToken.None, sendFile.Token);
Assert.Equal(CancellationToken.None, sendFile.Token);
var contentRange = new ContentRangeHeaderValue(start.Value, end.Value, 34);
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);

View File

@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -66,6 +65,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -148,6 +149,47 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal("File", body);
}
[Fact]
public async Task WriteFileAsync_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
var httpContext = GetHttpContext();
httpContext.Response.Body = new MemoryStream();
httpContext.RequestServices = new ServiceCollection()
.AddSingleton(appEnvironment.Object)
.AddTransient<IActionResultExecutor<VirtualFileResult>, TestVirtualFileResultExecutor>()
.AddTransient<ILoggerFactory, LoggerFactory>()
.BuildServiceProvider();
var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
var requestHeaders = httpContext.Request.GetTypedHeaders();
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
requestHeaders.Range = new RangeHeaderValue(0, 3);
requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
httpContext.Request.Method = HttpMethods.Get;
httpContext.Response.Body = new MemoryStream();
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
var httpResponse = actionContext.HttpContext.Response;
httpResponse.Body.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("FilePathResultTestFile contents¡", body);
}
[Fact]
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedIgnored()
{
@ -155,6 +197,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -186,7 +229,6 @@ namespace Microsoft.AspNetCore.Mvc
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Equal("FilePathResultTestFile contents¡", body);
}
@ -194,12 +236,13 @@ namespace Microsoft.AspNetCore.Mvc
[InlineData("0-5")]
[InlineData("bytes = ")]
[InlineData("bytes = 1-4, 5-11")]
public async Task WriteFileAsync_RangeRequestIgnored(string rangeString)
public async Task WriteFileAsync_RangeHeaderMalformed_RangeRequestIgnored(string rangeString)
{
// Arrange
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -228,7 +271,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
Assert.Equal("FilePathResultTestFile contents¡", body);
@ -243,6 +285,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -285,6 +328,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -313,7 +357,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status412PreconditionFailed, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -327,6 +370,7 @@ namespace Microsoft.AspNetCore.Mvc
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var result = new TestVirtualFileResult(path, contentType);
result.EnableRangeProcessing = true;
var appEnvironment = new Mock<IHostingEnvironment>();
appEnvironment.Setup(app => app.WebRootFileProvider)
.Returns(GetFileProvider(path));
@ -355,7 +399,6 @@ namespace Microsoft.AspNetCore.Mvc
var streamReader = new StreamReader(httpResponse.Body);
var body = streamReader.ReadToEndAsync().Result;
Assert.Equal(StatusCodes.Status304NotModified, httpResponse.StatusCode);
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
Assert.Null(httpResponse.ContentLength);
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
@ -454,6 +497,7 @@ namespace Microsoft.AspNetCore.Mvc
var result = new TestVirtualFileResult(path, "text/plain")
{
FileProvider = GetFileProvider(path),
EnableRangeProcessing = true,
};
var sendFile = new TestSendFileFeature();

View File

@ -2,11 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
@ -189,6 +189,24 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
[Fact]
public async Task FileFromDisk_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromDiskWithFileName");
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
// Act
var response = await Client.SendAsync(httpRequestMessage);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("This is a sample text file", body);
}
[Fact]
public async Task FileFromDisk_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest_WithLastModifiedAndEtag()
{
@ -259,11 +277,11 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var response = await Client.SendAsync(httpRequestMessage);
// Assert
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.NotNull(body);
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.Equal(expectedBody, body);
}
@ -299,12 +317,12 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Act
var response = await Client.SendAsync(httpRequestMessage);
var body = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Empty(body);
}
@ -329,6 +347,24 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
[Fact]
public async Task FileFromStream_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromStreamWithFileName");
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
// Act
var response = await Client.SendAsync(httpRequestMessage);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("This is sample text from a stream", body);
}
[Fact]
public async Task FileFromStream_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest()
{
@ -339,13 +375,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Act
var response = await Client.SendAsync(httpRequestMessage);
var body = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.NotNull(body);
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.Equal("This is", body);
}
@ -361,10 +397,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var response = await Client.SendAsync(httpRequestMessage);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("This is sample text from a stream", body);
}
@ -397,13 +431,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Act
var response = await Client.SendAsync(httpRequestMessage);
var body = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.NotNull(body);
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.Equal(expectedBody, body);
}
@ -439,12 +473,15 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Act
var response = await Client.SendAsync(httpRequestMessage);
var body = await response.Content.ReadAsStringAsync();
// Assert
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.NotNull(body);
Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Empty(body);
}
@ -470,7 +507,25 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}
[Fact]
public async Task FileFromBinaryData_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest()
public async Task FileFromBinaryData_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName");
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
// Act
var response = await Client.SendAsync(httpRequestMessage);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("This is a sample text from a binary array", body);
}
[Fact]
public async Task FileFromBinaryData_ReturnsFileWithFileName_IfRangeHeaderValid()
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName_WithEtag");
@ -479,13 +534,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Act
var response = await Client.SendAsync(httpRequestMessage);
var body = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
// Assert
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.NotNull(body);
Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
Assert.Equal("This is", body);
}
@ -557,6 +612,24 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
[Fact]
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
{
// Arrange
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EmbeddedFiles/DownloadFileWithFileName_RangeProcessingNotEnabled");
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
// Act
var response = await Client.SendAsync(httpRequestMessage);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("Sample text file as embedded resource.", body);
}
[Fact]
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest()
{

View File

@ -22,7 +22,7 @@ namespace FilesWebSite
public IActionResult DownloadFromDisk()
{
var path = Path.Combine(_hostingEnvironment.ContentRootPath, "sample.txt");
return PhysicalFile(path, "text/plain");
return PhysicalFile(path, "text/plain", true);
}
public IActionResult DownloadFromDisk_WithLastModifiedAndEtag()
@ -30,7 +30,7 @@ namespace FilesWebSite
var path = Path.Combine(_hostingEnvironment.ContentRootPath, "sample.txt");
var lastModified = new DateTimeOffset(year: 1999, month: 11, day: 04, hour: 3, minute: 0, second: 0, offset: new TimeSpan(0));
var entityTag = new EntityTagHeaderValue("\"Etag\"");
return PhysicalFile(path, "text/plain", lastModified, entityTag);
return PhysicalFile(path, "text/plain", lastModified, entityTag, true);
}
public IActionResult DownloadFromDiskWithFileName()
@ -44,7 +44,7 @@ namespace FilesWebSite
var path = Path.Combine(_hostingEnvironment.ContentRootPath, "sample.txt");
var lastModified = new DateTimeOffset(year: 1999, month: 11, day: 04, hour: 3, minute: 0, second: 0, offset: new TimeSpan(0));
var entityTag = new EntityTagHeaderValue("\"Etag\"");
return PhysicalFile(path, "text/plain", "downloadName.txt", lastModified, entityTag);
return PhysicalFile(path, "text/plain", "downloadName.txt", lastModified, entityTag, true);
}
public IActionResult DownloadFromStream()
@ -55,7 +55,7 @@ namespace FilesWebSite
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
return File(stream, "text/plain");
return File(stream, "text/plain", true);
}
public IActionResult DownloadFromStreamWithFileName()
@ -77,13 +77,13 @@ namespace FilesWebSite
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
var entityTag = new EntityTagHeaderValue("\"Etag\"");
return File(stream, "text/plain", "downloadName.txt", lastModified: null, entityTag: entityTag);
return File(stream, "text/plain", "downloadName.txt", lastModified: null, entityTag: entityTag, enableRangeProcessing: true);
}
public IActionResult DownloadFromBinaryData()
{
var data = Encoding.UTF8.GetBytes("This is a sample text from a binary array");
return File(data, "text/plain");
return File(data, "text/plain", true);
}
public IActionResult DownloadFromBinaryDataWithFileName()
@ -96,7 +96,7 @@ namespace FilesWebSite
{
var data = Encoding.UTF8.GetBytes("This is a sample text from a binary array");
var entityTag = new EntityTagHeaderValue("\"Etag\"");
return File(data, "text/plain", "downloadName.txt", lastModified: null, entityTag: entityTag);
return File(data, "text/plain", "downloadName.txt", lastModified: null, entityTag: entityTag, enableRangeProcessing: true);
}
}
}

View File

@ -14,7 +14,17 @@ namespace FilesWebSite
return new VirtualFileResult("/Greetings.txt", "text/plain")
{
FileProvider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "FilesWebSite.EmbeddedResources"),
FileDownloadName = "downloadName.txt"
FileDownloadName = "downloadName.txt",
EnableRangeProcessing = true,
};
}
public IActionResult DownloadFileWithFileName_RangeProcessingNotEnabled()
{
return new VirtualFileResult("/Greetings.txt", "text/plain")
{
FileProvider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "FilesWebSite.EmbeddedResources"),
FileDownloadName = "downloadName.txt",
};
}
@ -23,7 +33,8 @@ namespace FilesWebSite
var file = new VirtualFileResult("/Greetings.txt", "text/plain")
{
FileProvider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "FilesWebSite.EmbeddedResources"),
FileDownloadName = "downloadName.txt"
FileDownloadName = "downloadName.txt",
EnableRangeProcessing = true,
};
file.EntityTag = new Microsoft.Net.Http.Headers.EntityTagHeaderValue("\"Etag\"");