以块的forms下载文件(Windows Phone)

在我的应用程序中,我可以从网上下载一些媒体文件。 通常我使用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)我在下载此文件时遇到OutOfMemoryexception。 我想这是因为WebClient.OpenReadCompleted将整个流加载到RAM和chokes ……我需要更多的内存来解密这个流。

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

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

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

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

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

确认:您可以使用BackgroundTransferRequest下载多GB文件,但必须将TransferPreferences设置为None以强制在连接到外部电源时连接到wi-fi时发生下载,否则BackgroundTransferRequest将失败。


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

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

对于大于100 MB的文件,必须将传输的TransferPreferences属性设置为None,否则传输将失败。 如果您不知道传输的大小并且可能超出此限制,则应将该值设置为“无”,这意味着仅当手机连接到外部电源且具有Wi-Fi时才会进行传输连接。

关于使用“范围”动词的文档:

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

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

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