2013-10-24 24 views
11

我有一個應用程序,旨在從我們的本地數據庫流式傳輸視頻。昨天我花了很多時間嘗試將數據返回RangeFileContentResultRangeFileStreamResult而沒有成功。RangeFileContentResult和帶有遠程請求的視頻流

總之,當我將這兩個結果中的任何一個作爲返回文件時,我似乎無法獲得正確流式傳輸的視頻(或根本無法播放)。

來自瀏覽器的請求被用下面的頭部信息:

Range: bytes=0- 

並附帶提供的答覆讓這些標題爲例:

Accept-Ranges: bytes 
Content-Range: bytes 0-5103295/5103296 

在網絡流量方面,我得到一系列206的部分結果,然後200結束(根據fiddler)這似乎是正確的。 Chrome瀏覽器的網絡標籤不同意這一點,並看到一個初始請求(總是13個字節,我認爲這是一個握手),然後是多個狀態爲取消或掛起的請求。 據我瞭解,這或多或少是正確的,206 - 取消,206 - 取消等,但視頻不會播放。

如果我將結果從我的控制器切換到FileResult,視頻會播放和Chrome,IE10和Firefox,並且在下載結束之前似乎開始播放(這有點像流媒體!雖然我懷疑它不是)

但隨着範圍的結果,我沒有得到任何鉻或IE瀏覽器和整個視頻下載一滴在Firefox。

據我所知,RangeFileContentResult應處理響應客戶端的一系列字節下載(我似乎不這樣做,它只是告訴它獲取整個文件(由上面的響應說明) ))。客戶應該回應,但似乎沒有這樣做。

有沒有人在這方面有任何想法?具體來說:

a)RangeFileContentResult應該發送一個字節範圍回客戶端? b)有什麼辦法可以明確地控制從客戶端請求的字節範圍嗎? c)當請求RangeFileContentResult時,是否有任何原因或任何我在這裏做錯的事情,導致瀏覽器根本無法加載視頻?

編輯:增加了一個圖來幫助描述我所看到的:

RangedRequestImage

EDIT2:好了,情節變稠。雖然與RangedFile格賓斯玩弄我們需要進行推另一個系統的測試版本,我離開了「RangeFileContentResult」在我的控制器操作如下:

private ActionResult RetrieveVideo(MediaItem media) 
    { 
     return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now);    
    } 

而奇怪的是,這個現在看來工作我們天青如預期系統測試環境,但仍然不在我的本地機器上。我想知道是否有什麼基於IIS的東西在Azure IIS8上運行愉快,但不在我的本地7.5實例?

+0

嗨。我是Lib.Web.Mvc作者,我很樂意在這個特定的問題上挖掘。我們與iPad上的Safari有類似之處,這是由於它沒有上限地請求獲取關於該文件的信息而引起的請求。然後瀏覽器取消請求而不等待數據被傳輸(通常協議被瀏覽器侵犯)。這可能是類似的東西。請給我發一封電子郵件,告知您的環境詳情,以便我可以重新解決問題。你也可以在這裏找到Lib.Web.Mvc的完整源代碼和一些其他的東西:http:\\ tpeczek.codeplex.com。 – tpeczek

+0

@tpeczek嗨托馬斯,謝謝你,你能讓我知道最好的地址與你聯繫。我看到的主要奇怪的事情是從同一個版本加載之間的不一致。例如對於用戶1而言,它適用,對於用戶2則不適用。然後,用戶1刷新並工作,但用戶2刷新並且不刷新。讓我知道最好的地址,我會彈出一些細節。 – dougajmcdonald

+0

從我的個人資料:[email protected]。請提供儘可能詳細的信息(代碼,視頻,涉及的瀏覽器,主機配置,例外情況,任何日誌等) – tpeczek

回答

4

這裏所描述的問題的原因是傳遞給RangeFileContentResult構造modificationDate參數的值:

return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now); 

這個日期是爲了使用的RangeFileResult創建兩個頭:

  • ETag - 這個頭部是瀏覽器和服務器使用的標識符,以確保他們在講同一個實體。
  • Last-Modified - 此標題通知瀏覽器實體的最後修改日期。

一個DateTime.Now被傳遞每一個瀏覽器發出的部分請求可能是一個原因ETagLast-Modified頭值改變之前的客戶端將得到整個實體時,這一事實(通常,如果整個過程需要長於一秒)。

在上述情況下,瀏覽器正在發送帶請求的If-Range標頭。這個頭告訴服務器,如果實體標籤(或修改日期,因爲If-Range可以攜帶這兩個值中的任何一個)沒有太多,應該重新發送整個實體。這是發生在這種情況下。

事實上,修改日期是「動態的」,也可能會造成進一步的問題,如果客戶決定使用驗證以下標題之一:If-Modified-SinceIf-Unmodified-SinceIf-MatchIf-None-Match

這種情況下的解決方案是在文件數據庫中保留一個修改日期以確保它是一致的。

這裏還有一個優化的地方。每次發出部分請求時,不需要從數據庫中抓取整個視頻,而是可以緩存它或僅抓取相關部分(如果應用程序正在使用的數據庫引擎允許執行此操作)。可以使用這種機制,以便通過從RangeFileResult傳遞和覆蓋WriteEntireEntityWriteEntityRange方法來創建專門的行動結果。

+0

這是正確的答案,我使用DateTime.Now作爲修改日期,因爲我不明白參數的重要性。 作爲一個方面,圍繞此參數的一些文檔可能會很有用,就好像您不是在靜態文件流中一樣(因爲我們不是),所以您可能不會在每個文件中存儲「修改日期」。 – dougajmcdonald

+0

@dougajmcdonald如您所建議的文檔將被延長 – tpeczek

+0

您好托馬斯,再次感謝您的幫助排序這一個,真的很感謝所付出的努力。 – dougajmcdonald

3

mofiPlease只是在你的MVC項目這兩個文件複製
RangeFileResult
RangeFileStreamResult

public ActionResult Movie() 
{ 
    var path = new FileStream(@"C:\temp\01.avi", FileMode.Open); 
    return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now); 
} 

現在運行你的項目,並在Chrome中打開(例如:http://youraddress.com:45454/Main/Movie),你應該會看到你的文件播放使用標準鉻視頻播放器。它的流媒體,你可以看到它,如果你把一個斷點

return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now); 

再次來源是易於修改以改變它用於數據流的緩衝區大小!

+0

嗨,安東,謝謝你的迴應。稍微修改一下代碼之前的一個簡單問題。 我們的視頻存儲在數據庫中,我返回一個實體media.Content作爲一個字節[]和media.MimeType作爲一個字符串,我一直填充這些項目作爲參數'RangeFileContentResult'接受這個值,推測那也應該可以工作?或者特別需要打開文件流並使用RangeFileStreamResult? – dougajmcdonald

3

行,所以我沒有足夠的時間去看看RangeFileResult的細節,但我剛剛從 RangeFileContentResult

下載的文件(RangeFileContentResult)和修改我的代碼,所以它看起來像

public ActionResult Movie() 
{ 

    byte[] file = System.IO.File.ReadAllBytes(@"C:\HOME\asp\Java\Java EE. Programming Spring 3.0\01.avi"); 

    return new RangeFileContentResult(file, "video/x-msvideo", "01.avi", DateTime.Now); 

} 

並再次運作。然而,我注意到,當我停止視頻我有一個例外,它在RangeFileResult發生

if (context.HttpContext.Response.IsClientConnected) 
{ 
    WriteEntityRange(context.HttpContext.Response, RangesStartIndexes[i], RangesEndIndexes[i]); 
    if (MultipartRequest) 
       context.HttpContext.Response.Write("\r\n"); 
    context.HttpContext.Response.Flush(); 
} 

所以你最好修改代碼來處理it.In而言,當用戶已經斷開,但是你仍然要發送他們迴應。

再次,在技術上它不是一個很大的區別,你是否傳遞的byte []或流,因爲即使你通過流的碼與它一起工作

using (FileStream) 
      { 
       FileStream.Seek(rangeStartIndex, SeekOrigin.Begin); 

       int bytesRemaining = Convert.ToInt32(rangeEndIndex - rangeStartIndex) + 1; 
       byte[] buffer = new byte[_bufferSize]; 

       while (bytesRemaining > 0) 
       { 
        int bytesRead = FileStream.Read(buffer, 0, _bufferSize < bytesRemaining ? _bufferSize : bytesRemaining); 
        response.OutputStream.Write(buffer, 0, bytesRead); 
        bytesRemaining -= bytesRead; 
       } 
      } 

再次讀取數據,並將它們放入一個字節[]陣列!....所以這取決於你!

但是...我建議你注意你提供的內容類型!!! 的一點是,您的瀏覽器必須能夠處理它!所以,如果你提供的東西肯定不明,你將有problems.To找到您的內容類型的字符串請 mime-types-by-content-type

再次,我只是給了一個快速瀏覽一下,如果你有問題我以後會在回家後幫助你。

+0

再次感謝安東,稍後我會用細齒梳子通過它,讓你知道結果。 作爲一些方面,我已經從Nuget獲得了最新的Lib.Web.MVC,所以我希望能夠涵蓋文件的需求(儘管我會仔細檢查內容以確保我沒有舊副本)。 此外,我使用HttpPostedFileBase中的內容類型上傳視頻時存儲的內容類型。我接受並非所有的瀏覽器都會處理所有的擴展,但是如果我返回一個簡單的FileResult,它會呈現正確的測試視頻,但只是在一個塊中沒有搜索。 – dougajmcdonald