Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Add IFileInfo overloads for SendFileAsync. #555

Merged
merged 1 commit into from
Feb 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.FileProviders;

namespace Microsoft.AspNetCore.Http
{
Expand All @@ -15,13 +16,73 @@ namespace Microsoft.AspNetCore.Http
/// </summary>
public static class SendFileResponseExtensions
{
/// <summary>
/// Sends the given file using the SendFile extension.
/// </summary>
/// <param name="response"></param>
/// <param name="file">The file.</param>
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);
}

/// <summary>
/// Sends the given file using the SendFile extension.
/// </summary>
/// <param name="response"></param>
/// <param name="file">The file.</param>
/// <param name="offset">The offset in the file.</param>
/// <param name="count">The number of bytes to send, or null to send the remainder of the file.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
}

/// <summary>
/// Sends the given file using the SendFile extension.
/// </summary>
/// <param name="response"></param>
/// <param name="fileName">The full path to the file.</param>
/// <returns></returns>
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)
{
Expand All @@ -33,7 +94,7 @@ public static Task SendFileAsync(this HttpResponse response, string fileName)
throw new ArgumentNullException(nameof(fileName));
}

return response.SendFileAsync(fileName, 0, null, CancellationToken.None);
return response.SendFileAsync(fileName, 0, null, cancellationToken);
}

/// <summary>
Expand All @@ -42,10 +103,11 @@ public static Task SendFileAsync(this HttpResponse response, string fileName)
/// <param name="response"></param>
/// <param name="fileName">The full path to the file.</param>
/// <param name="offset">The offset in the file.</param>
/// <param name="count">The number of types to send, or null to send the remainder of the file.</param>
/// <param name="count">The number of bytes to send, or null to send the remainder of the file.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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)
{
Expand All @@ -67,21 +129,13 @@ public static Task SendFileAsync(this HttpResponse response, string fileName, lo
}

// 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;

Expand All @@ -96,8 +150,20 @@ private static async Task SendFileAsync(Stream outputStream, string fileName, lo
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);
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/Microsoft.AspNetCore.Http.Extensions/StreamCopyOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public static class StreamCopyOperation
{
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<byte>.Shared.Rent(DefaultBufferSize);
try
Expand All @@ -42,22 +42,22 @@ public static async Task CopyToAsync(Stream source, Stream destination, long? le
{
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
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.AspNetCore.Http.Extensions/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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-*"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}