// Copyright (c) .NET Foundation. All rights reserved.
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
namespace Microsoft.AspNet.Http
{
///
/// Provides extensions for HttpResponse exposing the SendFile extension.
///
public static class SendFileResponseExtensions
{
///
/// Sends the given file using the SendFile extension.
///
///
/// The full path to the file.
///
public static Task SendFileAsync(this HttpResponse response, string fileName)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (fileName == null)
{
throw new ArgumentNullException(nameof(fileName));
}
return response.SendFileAsync(fileName, 0, null, CancellationToken.None);
}
///
/// Sends the given file using the SendFile extension.
///
///
/// 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.
///
///
public static Task SendFileAsync(this HttpResponse response, string fileName, long offset, long? count, CancellationToken cancellationToken)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (fileName == null)
{
throw new ArgumentNullException(nameof(fileName));
}
var sendFile = response.HttpContext.Features.Get();
if (sendFile == null)
{
return SendFileAsync(response.Body, fileName, offset, count, cancellationToken);
}
return sendFile.SendFileAsync(fileName, offset, count, cancellationToken);
}
// Not safe for overlapped writes.
private static async Task SendFileAsync(Stream outputStream, string fileName, long offset, long? length, CancellationToken cancel)
{
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);
}
int bufferSize = 1024 * 16;
var fileStream = new FileStream(
fileName,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
bufferSize: bufferSize,
options: FileOptions.Asynchronous | FileOptions.SequentialScan);
using (fileStream)
{
fileStream.Seek(offset, SeekOrigin.Begin);
// TODO: Use buffer pool
var buffer = new byte[bufferSize];
await StreamCopyOperation.CopyToAsync(fileStream, buffer, outputStream, length, cancel);
}
}
}
}