diff --git a/src/Microsoft.AspNetCore.Http.Extensions/SendFileResponseExtensions.cs b/src/Microsoft.AspNetCore.Http.Extensions/SendFileResponseExtensions.cs
index 6058eb9a18..c747df187b 100644
--- a/src/Microsoft.AspNetCore.Http.Extensions/SendFileResponseExtensions.cs
+++ b/src/Microsoft.AspNetCore.Http.Extensions/SendFileResponseExtensions.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;
+using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Http
{
@@ -15,13 +16,73 @@ namespace Microsoft.AspNetCore.Http
///
public static class SendFileResponseExtensions
{
+ ///
+ /// Sends the given file using the SendFile extension.
+ ///
+ ///
+ /// The file.
+ public static Task SendFileAsync(this HttpResponse response, IFileInfo file,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (response == null)
+ {
+ throw new ArgumentNullException(nameof(response));
+ }
+ if (file == null)
+ {
+ throw new ArgumentNullException(nameof(file));
+ }
+
+ return response.SendFileAsync(file, 0, null, cancellationToken);
+ }
+
+ ///
+ /// Sends the given file using the SendFile extension.
+ ///
+ ///
+ /// The file.
+ /// The offset in the file.
+ /// The number of bytes to send, or null to send the remainder of the file.
+ ///
+ ///
+ public static async Task SendFileAsync(this HttpResponse response, IFileInfo file, long offset, long? count,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (response == null)
+ {
+ throw new ArgumentNullException(nameof(response));
+ }
+ if (file == null)
+ {
+ throw new ArgumentNullException(nameof(file));
+ }
+ CheckRange(offset, count, file.Length);
+
+ if (string.IsNullOrEmpty(file.PhysicalPath))
+ {
+ using (var fileContent = file.CreateReadStream())
+ {
+ if (offset > 0)
+ {
+ fileContent.Seek(offset, SeekOrigin.Begin);
+ }
+ await StreamCopyOperation.CopyToAsync(fileContent, response.Body, count, cancellationToken);
+ }
+ }
+ else
+ {
+ await response.SendFileAsync(file.PhysicalPath, offset, count, cancellationToken);
+ }
+ }
+
///
/// Sends the given file using the SendFile extension.
///
///
/// The full path to the file.
///
- public static Task SendFileAsync(this HttpResponse response, string fileName)
+ public static Task SendFileAsync(this HttpResponse response, string fileName,
+ CancellationToken cancellationToken = default(CancellationToken))
{
if (response == null)
{
@@ -33,7 +94,7 @@ namespace Microsoft.AspNetCore.Http
throw new ArgumentNullException(nameof(fileName));
}
- return response.SendFileAsync(fileName, 0, null, CancellationToken.None);
+ return response.SendFileAsync(fileName, 0, null, cancellationToken);
}
///
@@ -42,10 +103,11 @@ namespace Microsoft.AspNetCore.Http
///
/// The full path to the file.
/// The offset in the file.
- /// The number of types to send, or null to send the remainder of the file.
+ /// The number of bytes to send, or null to send the remainder of the file.
///
///
- public static Task SendFileAsync(this HttpResponse response, string fileName, long offset, long? count, CancellationToken cancellationToken)
+ public static Task SendFileAsync(this HttpResponse response, string fileName, long offset, long? count,
+ CancellationToken cancellationToken = default(CancellationToken))
{
if (response == null)
{
@@ -67,21 +129,13 @@ namespace Microsoft.AspNetCore.Http
}
// Not safe for overlapped writes.
- private static async Task SendFileAsync(Stream outputStream, string fileName, long offset, long? length, CancellationToken cancel)
+ private static async Task SendFileAsync(Stream outputStream, string fileName, long offset, long? count,
+ CancellationToken cancel = default(CancellationToken))
{
cancel.ThrowIfCancellationRequested();
var fileInfo = new FileInfo(fileName);
- if (offset < 0 || offset > fileInfo.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty);
- }
-
- if (length.HasValue &&
- (length.Value < 0 || length.Value > fileInfo.Length - offset))
- {
- throw new ArgumentOutOfRangeException(nameof(length), length, string.Empty);
- }
+ CheckRange(offset, count, fileInfo.Length);
int bufferSize = 1024 * 16;
@@ -96,8 +150,20 @@ namespace Microsoft.AspNetCore.Http
using (fileStream)
{
fileStream.Seek(offset, SeekOrigin.Begin);
+ await StreamCopyOperation.CopyToAsync(fileStream, outputStream, count, cancel);
+ }
+ }
- await StreamCopyOperation.CopyToAsync(fileStream, outputStream, length, cancel);
+ private static void CheckRange(long offset, long? count, long fileLength)
+ {
+ if (offset < 0 || offset > fileLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty);
+ }
+ if (count.HasValue &&
+ (count.Value < 0 || count.Value > fileLength - offset))
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), count, string.Empty);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Http.Extensions/StreamCopyOperation.cs b/src/Microsoft.AspNetCore.Http.Extensions/StreamCopyOperation.cs
index c42661a167..ef81ac3baf 100644
--- a/src/Microsoft.AspNetCore.Http.Extensions/StreamCopyOperation.cs
+++ b/src/Microsoft.AspNetCore.Http.Extensions/StreamCopyOperation.cs
@@ -15,9 +15,9 @@ namespace Microsoft.AspNetCore.Http.Extensions
{
private const int DefaultBufferSize = 4096;
- public static async Task CopyToAsync(Stream source, Stream destination, long? length, CancellationToken cancel)
+ public static async Task CopyToAsync(Stream source, Stream destination, long? count, CancellationToken cancel)
{
- long? bytesRemaining = length;
+ long? bytesRemaining = count;
var buffer = ArrayPool.Shared.Rent(DefaultBufferSize);
try
@@ -42,22 +42,22 @@ namespace Microsoft.AspNetCore.Http.Extensions
{
readLength = (int)Math.Min(bytesRemaining.Value, (long)readLength);
}
- int count = await source.ReadAsync(buffer, 0, readLength, cancel);
+ int read = await source.ReadAsync(buffer, 0, readLength, cancel);
if (bytesRemaining.HasValue)
{
- bytesRemaining -= count;
+ bytesRemaining -= read;
}
// End of the source stream.
- if (count == 0)
+ if (read == 0)
{
return;
}
cancel.ThrowIfCancellationRequested();
- await destination.WriteAsync(buffer, 0, count, cancel);
+ await destination.WriteAsync(buffer, 0, read, cancel);
}
}
finally
diff --git a/src/Microsoft.AspNetCore.Http.Extensions/project.json b/src/Microsoft.AspNetCore.Http.Extensions/project.json
index f38f4a0f48..a641249366 100644
--- a/src/Microsoft.AspNetCore.Http.Extensions/project.json
+++ b/src/Microsoft.AspNetCore.Http.Extensions/project.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0-*",
+ "Microsoft.Extensions.FileProviders.Abstractions": "1.0.0-*",
"Microsoft.Net.Http.Headers": "1.0.0-*",
"System.Buffers": "4.0.0-*"
},
diff --git a/src/Microsoft.AspNetCore.Http.Features/IHttpSendFileFeature.cs b/src/Microsoft.AspNetCore.Http.Features/IHttpSendFileFeature.cs
index 0776034e43..9418a7ffbe 100644
--- a/src/Microsoft.AspNetCore.Http.Features/IHttpSendFileFeature.cs
+++ b/src/Microsoft.AspNetCore.Http.Features/IHttpSendFileFeature.cs
@@ -8,6 +8,6 @@ namespace Microsoft.AspNetCore.Http.Features
{
public interface IHttpSendFileFeature
{
- Task SendFileAsync(string path, long offset, long? length, CancellationToken cancellation);
+ Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellation);
}
}
\ No newline at end of file