Asp.net-Core-Mvc
考慮到請求和響應範圍標頭,如何流式傳輸影片或文件?
我現在正在使用
FileStreamResult它,它可以流式傳輸影片,但無法尋找它。它總是從頭開始。我正在使用
ByteRangeStreamContent,但它似乎不再適用於dnxcore50.那麼如何進行呢?
我是否需要手動解析請求範圍標頭並編寫一個自定義
FileResult來設置響應Content-Range和其餘標頭並將緩衝區範圍寫入響應正文,或者是否已經實現了某些東西並且我錯過了它?
這是 a 的簡單實現
VideoStreamResult。我目前正在使用(未測試多部分內容部分):public class VideoStreamResult : FileStreamResult { // default buffer size as defined in BufferedStream type private const int BufferSize = 0x1000; private string MultipartBoundary = "<qwe123>"; public VideoStreamResult(Stream fileStream, string contentType) : base(fileStream, contentType) { } public VideoStreamResult(Stream fileStream, MediaTypeHeaderValue contentType) : base(fileStream, contentType) { } private bool IsMultipartRequest(RangeHeaderValue range) { return range != null && range.Ranges != null && range.Ranges.Count > 1; } private bool IsRangeRequest(RangeHeaderValue range) { return range != null && range.Ranges != null && range.Ranges.Count > 0; } protected async Task WriteVideoAsync(HttpResponse response) { var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>(); bufferingFeature?.DisableResponseBuffering(); var length = FileStream.Length; var range = response.HttpContext.GetRanges(length); if (IsMultipartRequest(range)) { response.ContentType = $"multipart/byteranges; boundary={MultipartBoundary}"; } else { response.ContentType = ContentType.ToString(); } response.Headers.Add("Accept-Ranges", "bytes"); if (IsRangeRequest(range)) { response.StatusCode = (int)HttpStatusCode.PartialContent; if (!IsMultipartRequest(range)) { response.Headers.Add("Content-Range", $"bytes {range.Ranges.First().From}-{range.Ranges.First().To}/{length}"); } foreach (var rangeValue in range.Ranges) { if (IsMultipartRequest(range)) // I don't know if multipart works { await response.WriteAsync($"--{MultipartBoundary}"); await response.WriteAsync(Environment.NewLine); await response.WriteAsync($"Content-type: {ContentType}"); await response.WriteAsync(Environment.NewLine); await response.WriteAsync($"Content-Range: bytes {range.Ranges.First().From}-{range.Ranges.First().To}/{length}"); await response.WriteAsync(Environment.NewLine); } await WriteDataToResponseBody(rangeValue, response); if (IsMultipartRequest(range)) { await response.WriteAsync(Environment.NewLine); } } if (IsMultipartRequest(range)) { await response.WriteAsync($"--{MultipartBoundary}--"); await response.WriteAsync(Environment.NewLine); } } else { await FileStream.CopyToAsync(response.Body); } } private async Task WriteDataToResponseBody(RangeItemHeaderValue rangeValue, HttpResponse response) { var startIndex = rangeValue.From ?? 0; var endIndex = rangeValue.To ?? 0; byte[] buffer = new byte[BufferSize]; long totalToSend = endIndex - startIndex; int count = 0; long bytesRemaining = totalToSend + 1; response.ContentLength = bytesRemaining; FileStream.Seek(startIndex, SeekOrigin.Begin); while (bytesRemaining > 0) { try { if (bytesRemaining <= buffer.Length) count = FileStream.Read(buffer, 0, (int)bytesRemaining); else count = FileStream.Read(buffer, 0, buffer.Length); if (count == 0) return; await response.Body.WriteAsync(buffer, 0, count); bytesRemaining -= count; } catch (IndexOutOfRangeException) { await response.Body.FlushAsync(); return; } finally { await response.Body.FlushAsync(); } } } public override async Task ExecuteResultAsync(ActionContext context) { await WriteVideoAsync(context.HttpContext.Response); } }並解析請求標頭範圍:
public static RangeHeaderValue GetRanges(this HttpContext context, long contentSize) { RangeHeaderValue rangesResult = null; string rangeHeader = context.Request.Headers["Range"]; if (!string.IsNullOrEmpty(rangeHeader)) { // rangeHeader contains the value of the Range HTTP Header and can have values like: // Range: bytes=0-1 * Get bytes 0 and 1, inclusive // Range: bytes=0-500 * Get bytes 0 to 500 (the first 501 bytes), inclusive // Range: bytes=400-1000 * Get bytes 500 to 1000 (501 bytes in total), inclusive // Range: bytes=-200 * Get the last 200 bytes // Range: bytes=500- * Get all bytes from byte 500 to the end // // Can also have multiple ranges delimited by commas, as in: // Range: bytes=0-500,600-1000 * Get bytes 0-500 (the first 501 bytes), inclusive plus bytes 600-1000 (401 bytes) inclusive // Remove "Ranges" and break up the ranges string[] ranges = rangeHeader.Replace("bytes=", string.Empty).Split(",".ToCharArray()); rangesResult = new RangeHeaderValue(); for (int i = 0; i < ranges.Length; i++) { const int START = 0, END = 1; long endByte, startByte; long parsedValue; string[] currentRange = ranges[i].Split("-".ToCharArray()); if (long.TryParse(currentRange[END], out parsedValue)) endByte = parsedValue; else endByte = contentSize - 1; if (long.TryParse(currentRange[START], out parsedValue)) startByte = parsedValue; else { // No beginning specified, get last n bytes of file // We already parsed end, so subtract from total and // make end the actual size of the file startByte = contentSize - endByte; endByte = contentSize - 1; } rangesResult.Ranges.Add(new RangeItemHeaderValue(startByte, endByte)); } } return rangesResult; }