適用於mp4視頻流無法通過URL作爲靜態內容返回的場景。
環境:
- 服務端 .Net MVC。
- 客戶端 Chrome瀏覽器
- 播放環境 Video原生標籤
- MP4物理路徑無法通過IIS請求到,需通過程序讀取到內存然後返回。(如果這個MP4是靜態內容,不需要程序處理即IIS直接處理,則一般不會有這個問題)
問題:無法進行拖放或者拖放以後要重頭進行播放。
故障原因:與Video標籤適配的請求未被響應。
如何做:
-
Chrome瀏覽器默認請求會在 Header 添加一個Range如下:服務器要做的就是響應這個Header
Range: bytes=0-
-
服務器接收到這個Header以後,響應的Header如下,同時需要設置狀態碼爲206:
Accept-Ranges: bytes Content-Length: 2097152 Content-Range: bytes 0-2097151/38696534
其中需要說明的是,這裏服務端設置了響應2MB(210241024=2097152字節)的分段加載,返回的數據流則是文件流索引0-2097151的內容,因爲文件流索引是從0開始的。38696534 是視頻文件的總長度。
帖服務器端響應代碼:
public ActionResult Video(Guid id)
{
//數據庫存儲的文件路徑
var archive = ManagerFactory.ArchiveManager.GetVideoArchive(id);
//物理絕對路徑
var path = Server.MapPath("~/" + archive.Path);
//設置響應頭
HttpContext.Response.Headers.Add("Accept-Ranges", "bytes");
//判定是否響應Chrome中的Range請求
var rangeQuery = HttpContext.Request.Headers["Range"];
if (rangeQuery.IsNotNullAndNotEmpty())
{
//startIndex 與 endIndex 是索引值,是0開始的,注意在與 Length 比較的時候 Length 要 -1
var startIndex= 0;
//視頻每節長度爲2MB
var length = 2 * 1024 * 1024;
var range = rangeQuery.Split('=')[1].Split('-');
startIndex= range[0].ParseToInt();
var endIndex = startIndex + length - 1;
var fileLength = (int)archive.Length;
//是否指定了索引終結點
if (range[1].IsNotNullAndNotEmpty())
{
endIndex = range[1].ParseToInt();
}
if (endIndex <= startIndex )
{
endIndex = startIndex + length - 1;
}
if (endIndex > fileLength - 1)
{
endIndex = fileLength - 1;
}
//關鍵設置
HttpContext.Response.Headers.Add("Content-Range", $"bytes {startIndex}-{endIndex}/{archive.Length}");
//關鍵設置
HttpContext.Response.StatusCode = 206;
//按照索引讀取文件流
using (var fs = System.IO.File.OpenRead(path))
{
var buffer = new byte[endIndex - startIndex + 1];
fs.Position = startIndex ;
fs.Read(buffer, 0, buffer.Length);
return File(buffer, archive.ContentType);
}
}
//如果未設置則返回整個文件
return File(path, archive.ContentType);
}