6

私のアプリケーションでは、Web からいくつかのメディア ファイルをダウンロードできます。通常、私はWebClient.OpenReadCompletedメソッドを使用して、ファイルをダウンロードし、復号化して、IsolatedStorage に保存しました。それはうまく機能し、次のようになりました。

 private void downloadedSong_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e, SomeOtherValues someOtherValues) // delegate, uses additional values
        {
            // Some preparations

                try
                {
                   if (e.Result != null)
                   {
                    using (isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        // working with the gained stream, decryption
                        // saving the decrypted file to isolatedStorage
                        isolatedStorageFileStream = new IsolatedStorageFileStream("SomeFileNameHere", FileMode.OpenOrCreate, isolatedStorageFile);
                        // and use it for MediaElement
                        mediaElement.SetSource(isolatedStorageFileStream);
                        mediaElement.Position = new TimeSpan(0);
                        mediaElement.MediaOpened += new RoutedEventHandler(mediaFile_MediaOpened);

                        // and some other work
                     }
                    }
                 }
                 catch(Exception ex) 
                 {
                  // try/catch stuff
                 }
           }

しかし、調査の結果、大きなファイル (私にとっては 100 MB 以上) を使用すると、このファイルのダウンロード中にOutOfMemory例外が発生することがわかりました。これは、WebClient.OpenReadCompleted がストリーム全体を RAM にロードしてチョークするためだと思います...そして、このストリームを復号化するには、より多くのメモリが必要になります。

別の調査の後、このファイルをIsolatedStorageに保存する際のOpenReadCompletedイベント(または復号化してから保存する)の後に大きなファイルをチャンクに分割する方法を見つけましたが、これは問題の一部にしか役立ちません...プライマリ問題は、ダウンロード プロセス中に電話が詰まらないようにする方法です。大きなファイルをチャンクでダウンロードする方法はありますか? 次に、見つかったソリューションを使用して、復号化プロセスを通過できます。(それでも、そのような大きなファイルをmediaElementにロードする方法を見つける必要がありますが、それは別の問題です)


答え:

 private WebHeaderCollection headers;
 private int iterator = 0;
 private int delta = 1048576;
 private string savedFile = "testFile.mp3";

 // some preparations
 // Start downloading first piece


using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        if (isolatedStorageFile.FileExists(savedFile))
                            isolatedStorageFile.DeleteFile(savedFile);
                    }

                    headers = new WebHeaderCollection();
                    headers[HttpRequestHeader.Range] = "bytes=" + iterator.ToString() + '-' + (iterator + delta).ToString();
                    webClientReadCompleted = new WebClient();
                    webClientReadCompleted.Headers = headers;
                    webClientReadCompleted.OpenReadCompleted += downloadedSong_OpenReadCompleted;
                    webClientReadCompleted.OpenReadAsync(new Uri(song.Link));
                    // song.Link was given earlier

private void downloadedSong_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            try
            {
                if (e.Cancelled == false)
                {
                    if (e.Result != null)
                    {
                        ((WebClient)sender).OpenReadCompleted -= downloadedSong_OpenReadCompleted;

                        using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
                        {
                            using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(savedFile, FileMode.Append, FileAccess.Write, myIsolatedStorage))
                            {
                                int mediaFileLength = (int)e.Result.Length;
                                byte[] byteFile = new byte[mediaFileLength];
                                e.Result.Read(byteFile, 0, byteFile.Length);
                                fileStream.Write(byteFile, 0, byteFile.Length); 

                                // If there's something left, download it recursively
                                if (byteFile.Length > delta)
                                {
                                    iterator = iterator + delta + 1;

                                    headers = new WebHeaderCollection();
                                    headers[HttpRequestHeader.Range] = "bytes=" + iterator.ToString() + '-' + (iterator + delta).ToString();
                                    webClientReadCompleted.Headers = headers;
                                    webClientReadCompleted.OpenReadCompleted += downloadedSong_OpenReadCompleted;
                                    webClientReadCompleted.OpenReadAsync(new Uri(song.Link));
                                }
                            }
                        }
                    }
                }
            }
4

2 に答える 2

4

ファイルをチャンクでダウンロードするには、複数のリクエストを行う必要があります。チャンクごとに1つ。
残念ながら、「このファイルを取得して、サイズXのチャンクで返す」と言うことはできません。

サーバーがそれをサポートしていると仮定すると、HTTPRangeヘッダーを使用して、サーバーが要求に応答して返すファイルのバイトを指定できます。
次に、ファイルを分割して取得するために複数のリクエストを行い、それをすべてデバイスに戻します。前のチャンクを取得して検証したら、順次呼び出しを行い、次の呼び出しを開始するのがおそらく最も簡単です。

このアプローチにより、ユーザーがアプリに戻ったときにダウンロードを簡単に再開できます。以前にダウンロードされた量を確認してから、次のチャンクを取得します。

映画(最大2.6GB)を64Kのチャンクでダウンロードし、IsolatedStorageから。を使用して再生するアプリを作成しましたMediaPlayerLauncher。経由でのプレイMediaElementも機能するはずですが、確認していません。これをテストするには、大きなファイルを(Isolated Storage Explorerなどを介して)IsolatedStorageに直接ロードし、その方法で再生した場合のメモリへの影響を確認します。

于 2013-01-30T12:49:40.403 に答える
1

確認済み:を使用BackgroundTransferRequestしてマルチ GB ファイルをダウンロードできますが、外部電源に接続されている間、および Wi-Fi に接続されている間にダウンロードを強制的に実行するように設定TransferPreferencesする必要があります。そうしないと失敗します。NoneBackgroundTransferRequest


BackgroundTransferRequest を使用して大きなファイルを簡単にダウンロードし、電話に実装の詳細を心配させることは可能でしょうか? ドキュメントは、100 MB を超えるファイルのダウンロードが可能であることを示唆しているようで、「Range」動詞は独自の使用のために予約されているため、バックグラウンドで可能であれば、おそらくこれを自動的に使用します。

100 MB を超えるファイルに関するドキュメントから:

100 MB を超えるファイルの場合、転送の TransferPreferences プロパティを None に設定する必要があります。そうしないと、転送は失敗します。転送のサイズがわからず、この制限を超える可能性がある場合は、値を [なし] に設定する必要があります。これは、電話が外部電源に接続され、Wi-Fi がある場合にのみ転送が続行されることを意味します。繋がり。

「Range」動詞の使用に関するドキュメントから:

BackgroundTransferRequest オブジェクトの Headers プロパティは、転送要求の HTTP ヘッダーを設定するために使用されます。次のヘッダーは、システムで使用するために予約されており、呼び出し元のアプリケーションでは使用できません。次のいずれかのヘッダーを Headers コレクションに追加すると、Add(BackgroundTransferRequest) メソッドを使用して転送要求をキューに入れるときに、NotSupportedException がスローされます。

  • If-Modified-Since
  • 一致しない場合
  • If-Range
  • 範囲
  • 変更されていない限り

ドキュメントは次のとおりです 。 http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202955(v=vs.105).aspx

于 2014-05-13T21:53:34.603 に答える