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

Commit

Permalink
#527 Add IFileInfo overloads for SendFileAsync.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tratcher committed Feb 8, 2016
1 parent 3e6c717 commit 04e9da4
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 23 deletions.
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);
}
}

0 comments on commit 04e9da4

Please sign in to comment.