public async Task SendAsync() { ApplyResponseHeaders(Constants.Status200Ok); string physicalPath = _fileInfo.PhysicalPath; var sendFile = _context.Features.Get <IHttpSendFileFeature>(); if (sendFile != null && !string.IsNullOrEmpty(physicalPath)) { await sendFile.SendFileAsync(physicalPath, 0, _length, _context.RequestAborted); return; } Stream readStream = _fileInfo.CreateReadStream(); try { await StreamCopyOperation.CopyToAsync(readStream, _response.Body, _length, _context.RequestAborted); } finally { readStream.Dispose(); } }
// When there is only a single range the bytes are sent directly in the body. internal async Task SendRangeAsync() { bool rangeNotSatisfiable = false; if (_ranges.Count == 0) { rangeNotSatisfiable = true; } if (rangeNotSatisfiable) { // 14.16 Content-Range - A server sending a response with status code 416 (Requested range not satisfiable) // SHOULD include a Content-Range field with a byte-range-resp-spec of "*". The instance-length specifies // the current length of the selected resource. e.g. */length _responseHeaders.ContentRange = new ContentRangeHeaderValue(_length); ApplyResponseHeaders(Constants.Status416RangeNotSatisfiable); _logger.LogWarning("Range not satisfiable for {0}", SubPath); return; } // Multi-range is not supported. Debug.Assert(_ranges.Count == 1); long start, length; _responseHeaders.ContentRange = ComputeContentRange(_ranges[0], out start, out length); _response.ContentLength = length; ApplyResponseHeaders(Constants.Status206PartialContent); string physicalPath = _fileInfo.PhysicalPath; var sendFile = _context.Features.Get <IHttpSendFileFeature>(); if (sendFile != null && !string.IsNullOrEmpty(physicalPath)) { if (_logger.IsEnabled(LogLevel.Verbose)) { _logger.LogVerbose(string.Format("Sending {0} of file {1}", _response.Headers[HeaderNames.ContentRange], physicalPath)); } await sendFile.SendFileAsync(physicalPath, start, length, _context.RequestAborted); return; } Stream readStream = _fileInfo.CreateReadStream(); try { readStream.Seek(start, SeekOrigin.Begin); // TODO: What if !CanSeek? if (_logger.IsEnabled(LogLevel.Verbose)) { _logger.LogVerbose(string.Format("Copying {0} of file {1} to the response body", _response.Headers[HeaderNames.ContentRange], SubPath)); } await StreamCopyOperation.CopyToAsync(readStream, _response.Body, length, _context.RequestAborted); } finally { readStream.Dispose(); } }
// Not safe for overlapped writes. public async Task SendFileAsync(string fileName, long offset, long?length, CancellationToken cancel) { cancel.ThrowIfCancellationRequested(); if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentNullException(nameof(fileName)); } if (!File.Exists(fileName)) { throw new FileNotFoundException(string.Empty, fileName); } 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); } var fileStream = new FileStream( fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize: 1024 * 64, options: FileOptions.Asynchronous | FileOptions.SequentialScan); try { fileStream.Seek(offset, SeekOrigin.Begin); _logger.LogCopyingBytesToResponse( start: offset, end: length != null ? (offset + length) : null, path: fileName); await StreamCopyOperation.CopyToAsync(fileStream, _output, length, cancel); } finally { fileStream.Dispose(); } }
// Not safe for overlapped writes. public async Task SendFileAsync(string fileName, long offset, long?length, CancellationToken cancel) { cancel.ThrowIfCancellationRequested(); if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentNullException(nameof(fileName)); } if (!File.Exists(fileName)) { throw new FileNotFoundException(string.Empty, fileName); } var fileInfo = new FileInfo(fileName); if (offset < 0 || offset > fileInfo.Length) { throw new ArgumentOutOfRangeException("offset", offset, string.Empty); } if (length.HasValue && (length.Value < 0 || length.Value > fileInfo.Length - offset)) { throw new ArgumentOutOfRangeException("length", length, string.Empty); } #if DNX451 Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024 * 64, FileOptions.Asynchronous | FileOptions.SequentialScan); #else // TODO: Bring back async when the contract gets it Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024 * 64); #endif try { fileStream.Seek(offset, SeekOrigin.Begin); if (_logger.IsEnabled(LogLevel.Verbose)) { _logger.LogVerbose(string.Format("Copying bytes {0}-{1} of file {2} to response body", offset, length != null ? (offset + length).ToString() : "*", fileName)); } await StreamCopyOperation.CopyToAsync(fileStream, _output, length, cancel); } finally { fileStream.Dispose(); } }