以块的形式下载文件(Windows Phone)

时间:2013-01-30 10:49:17

标签: c# windows-phone-7 download chunking

在我的应用程序中,我可以从网上下载一些媒体文件。通常我使用 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中并且扼流......我需要更多内存来解密这个流。

经过另一次调查后,我发现在将OpenReadCompleted事件保存到IsolatedStorage(或解密然后保存在我的ocasion中)之后,如何将大文件分成块,但这只会导致问题的一部分。 。主要问题是如何在下载过程中防止手机窒息有没有办法以块的形式下载大文件?然后我可以使用找到的解决方案来通过解密过程。 (而且我仍然需要找到一种方法将这么大的文件加载到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));
                                }
                            }
                        }
                    }
                }
            }

2 个答案:

答案 0 :(得分:4)

要以块的形式下载文件,您需要发出多个请求。每个块一个。
不幸的是,我不可能说#34;给我这个文件并将其返回到大小为X"

的块中

假设服务器支持它,您可以使用HTTP Range标头指定服务器应响应请求返回的文件的哪些字节。
然后,您发出多个请求以将文件分成几部分,然后将它们全部重新放在设备上。在您获得并验证上一个块之后,您可能会发现最简单的顺序调用并启动下一个调用。

这种方法可以让用户在返回应用程序时恢复下载变得简单。你只需看看先前下载了多少,然后获得下一个块。

我已经编写了一个以64K块下载电影(最高2.6GB)的应用程序,然后使用MediaPlayerLauncher从IsolatedStorage播放它们。通过MediaElement玩也应该有效,但我还没有得到验证。您可以通过将大文件直接加载到IsolatedStorage(通过Isolated Storage Explorer或类似文件)来测试这一点,并检查以这种方式播放的内存含义。

答案 1 :(得分:1)

已确认:您可以使用BackgroundTransferRequest下载多GB文件,但必须将TransferPreferences设置为None才能在连接时强制下载一个外部电源,当连接到Wi-Fi时,BackgroundTransferRequest将失败。


我想知道是否可以使用BackgroundTransferRequest轻松下载大文件并让手机担心实施细节?文档似乎表明文件下载超过100 MB是可能的,"范围"动词是为它自己使用而保留的,所以它可能会在幕后自动使用它。

有关超过100 MB的文件的文档:

  

对于大于100 MB的文件,您必须设置TransferPreferences   转移到无或转移的财产将失败。如果你这样做   不知道转移的大小,它有可能   超过此限制,您应该将值设置为None,这意味着   转移仅在手机连接到外部时才会进行   电源并具有Wi-Fi连接。

关于使用" Range"的文档。动词:

  

使用BackgroundTransferRequest对象的Headers属性   设置传输请求的HTTP标头。以下标题   保留供系统使用,不能通过调用使用   应用。将以下标题之一添加到标题   集合将导致在抛出NotSupportedException时抛出   添加(BackgroundTransferRequest)方法用于对传输进行排队   请求:

     
      
  • 如果-Modified-Since的
  •   
  • 如果-无 - 匹配
  •   
  • 如果-范围
  •   
  • 范围
  •   
  • 除非-Modified-Since的
  •   

以下是文档: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202955(v=vs.105).aspx