14

ローカル DB からビデオをストリーミングすることを目的としたアプリケーションがあります。昨日、データを aRangeFileContentResultまたはRangeFileStreamResult成功せずに返そうとするのに多くの時間を費やしました。

つまり、これら 2 つの結果のいずれかとしてファイルを返すと、ビデオを正しくストリーミング (またはまったく再生) できないようです。

ブラウザからのリクエストは、次のヘッダーとともに送信されます。

Range: bytes=0-

応答が提供され、例としてこれらのヘッダーが提供されます。

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

ネットワーク トラフィックに関しては、部分的な結果に対して一連の 206 を取得し、最後に (フィドラーによると) 200 を取得しますが、これは正しいようです。Chrome のネットワーク タブはこれに同意せず、最初のリクエスト (常に 13 バイトで、これはハンドシェイクであると想定しています) に続いて、ステータスがキャンセルまたは保留になっているいくつかのリクエストが表示されます。私が理解している限り、これは多かれ少なかれ正しい、206 - キャンセル、206 - キャンセルなどですが、ビデオは再生されません。

結果をコントローラーから FileResult に切り替えると、ビデオが再生され、ダウンロードが完了する前に Chrome、IE10、および Firefox が再生を開始するように見えます (ストリーミングしているように少し感じますが、そうではないのではないかと思います)。

しかし、範囲の結果では、クロムまたはIEでは何も得られず、ビデオ全体がFirefoxで1回のドロップでダウンロードされます。

私が理解している限り、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);            
}

奇妙なことに、これは現在、Azure システム テスト環境では期待どおりに機能しているように見えますが、ローカル マシンではまだ機能していません。Azures IIS8 では問題なく動作するが、私のローカル 7.5 インスタンスでは動作しない IIS ベースの何かがあるのだろうか?

4

3 に答える 3

6

ここで説明する問題の理由は、コンストラクターmodificationDateのパラメーターに渡される値です。RangeFileContentResult

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

この日付は、次のRangeFileResult2 つのヘッダーを作成するために使用されます。

  • ETag- このヘッダーは、ブラウザとサーバーが同じエンティティについて話していることを確認するために使用される識別子です。
  • Last-Modified- このヘッダーは、エンティティの最終変更日をブラウザに通知します。

DateTime.Nowブラウザが部分的なリクエストを行うたびにa が渡されるという事実は、クライアントがエンティティ全体を取得する前にヘッダー値が変更される理由ETagとなる可能性がありLast-Modifiedます (通常、プロセス全体に 1 秒以上かかる場合)。

上記の場合、ブラウザはIf-Rangeリクエストとともにヘッダーを送信しています。If-Rangeこのヘッダーは、エンティティ タグ (またはこれらの 2 つの値のいずれかを保持できるため、変更日) がそれほど多くない場合、エンティティ全体を再送信する必要があることをサーバーに伝えています。これがこの場合に起こることです。

変更日が「動的」であるという事実は、クライアントが検証のために次のヘッダーのいずれかを使用することを決定した場合、さらに問題を引き起こす可能性があります: If-Modified-Since, If-Unmodified-Since, .If-MatchIf-None-Match

この状況での解決策は、ファイルと一緒にデータベースに変更日を保持して、一貫性を保つことです。

ここにも最適化の場所があります。部分的なリクエストが行われるたびに DB からビデオ全体を取得する代わりに、ビデオをキャッシュするか、関連する部分のみを取得することができます (アプリケーションが使用しているデータベース エンジンがそのような操作を許可している場合)。このようなメカニズムを使用して、andメソッドから配信RangeFileResultおよび上書きすることにより、特殊なアクション結果を作成できます。WriteEntireEntityWriteEntityRange

于 2013-11-27T15:01:58.500 に答える
4

わかりましたので、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();
}

そのため、コードを変更して処理することをお勧めします。ユーザーが既に切断されているが、まだ応答を送信しようとしている場合。

繰り返しますが、技術的には、 byte[] を渡すか Stream を渡すかは大きな違いではありません。なぜなら、 Stream を渡す場合でも、コードはそれで動作するからです。

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;
    }
}

再びデータを読み取り、それらを byte[] 配列に入れます!....それはあなた次第です!

しかし...提供するコンテンツタイプに注意することをお勧めします!!! ポイントは、ブラウザがそれを処理できる必要があるということです!したがって、何か不明なものを指定すると、問題が発生することは間違いありません.コンテンツ タイプの文字列を見つけるには、 mime-types-by-content-typeを確認してください。

繰り返しますが、簡単に見てみました。問題がある場合は、後で家に帰ってからお手伝いします。

于 2013-11-05T22:41:26.233 に答える