以块形式下载文件(Windows Phone) [英] Download file in chunks (Windows Phone)

查看:136
本文介绍了以块形式下载文件(Windows Phone)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我可以从网上下载一些媒体文件。通常我使用 WebClient.OpenReadCompleted 方法来下载,解密并将文件保存到IsolatedStorage。它工作得很好,看起来像这样:

  private void downloadedSong_OpenReadCompleted(object sender,OpenReadCompletedEventArgs e,SomeOtherValues someOtherValues)// delegate,uses附加值
{
//一些准备

尝试
{
if(e.Result!= null)
{
使用(isolationStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
//使用获取的流,解密
//将解密的文件保存到isolationStorage
isolatedStorageFileStream = new IsolatedStorageFileStream SomeFileNameHere,FileMode.OpenOrCreate,isolationStorageFile);
//并将其用于MediaElement
mediaElement.SetSource(isolatedStorageFileStream);
mediaElement.Position = new TimeSpan(0);
mediaElement.MediaOpened + = new RoutedEventHandler(mediaFile_MediaOpened);

//和一些其他工作
}
}
}
catch(Exception ex)
{
// try / catch东西
}
}

但经过一番调查,我发现与大文件(对我来说它超过100 MB)下载此文件时,我将获得 OutOfMemory 异常。我想这是因为WebClient.OpenReadCompleted将整个流加载到RAM和扼流圈...我需要更多的内存来解密这个流。



经过另一个调查, ve在OpenReadCompleted事件之后,将大文件分割成块,将其保存到IsolatedStorage(或解密然后保存在我的ocasion中),但这只会帮助解决问题的一部分...主要问题是如何以防止下载过程中的电话扼流圈。 是否有一种方式可以大量下载大文件?然后我可以使用找到的解决方案来通过解密过程。 (仍然需要找到一种方式将这样的大文件加载到mediaElement中,但这将是另一个问题)






答案:

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

//一些准备工作
//开始下载第一件


使用(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以前提供

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

使用(IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
使用(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(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));
}
}
}
}
}
}


解决方案

要以块形式下载文件,您需要进行多个请求。一个为每个块。

不幸的是,不可能说让我这个文件,并以大小X返回;



假设服务器支持它,您可以使用HTTP Range 头来指定服务器响应请求返回的文件的哪些字节。

您然后进行多个请求以将文件分片,然后将其全部放回设备上。一旦你获得并验证了以前的块,你可能会发现顺序呼叫最简单,并启动下一个。



这种方法使得简单的恢复当用户返回到应用程序时下载。你只是看看以前下载了多少,然后得到下一个块。



我已经编写了一个应用程序,它在64K块中下载电影(高达2.6GB),然后从IsolatedStorage中使用 MediaPlayerLauncher 。通过 MediaElement 播放应该也可以,但是我还没有验证。您可以通过将大型文件直接加载到IsolatedStorage(通过隔离存储资源管理器或类似文件)进行测试,并检查播放内存的含义。


In my application I can download some media files from web. Normally I used WebClient.OpenReadCompleted method to download, decrypt and save the file to IsolatedStorage. It worked well and looked like that:

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

But after some investigation I found out that with large files(for me it's more than 100 MB) I'm getting OutOfMemory exception during downloading this file. I suppose that's because WebClient.OpenReadCompleted loads the whole stream into RAM and chokes... And I will need more memory to decrypt this stream.

After another investigation, I've found how to divide large file into chunks after OpenReadCompleted event at saving this file to IsolatedStorage(or decryption and then saving in my ocasion), but this would help with only a part of problem... The primary problem is how to prevent phone chokes during download process. Is there a way to download large file in chunks? Then I could use the found solution to pass through decryption process. (and still I'd need to find a way to load such big file into mediaElement, but that would be another question)


Answer:

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

解决方案

To download a file in chunks you'll need to make multiple requests. One for each chunk.
Unfortunately it's not possible to say "get me this file and return it in chunks of size X";

Assuming that the server supports it, you can use the HTTP Range header to specify which bytes of a file the server should return in response to a request.
You then make multiple requests to get the file in pieces and then put it all back together on the device. You'll probably find it simplest to make sequential calls and start the next one once you've got and verified the previous chunk.

This approach makes it simple to resume a download when the user returns to the app. You just look at how much was downloaded previously and then get the next chunk.

I've written an app which downloads movies (up to 2.6GB) in 64K chunks and then played them back from IsolatedStorage with the MediaPlayerLauncher. Playing via the MediaElement should work too but I haven't verified. You can test this by loading a large file directly into IsolatedStorage (via Isolated Storage Explorer, or similar) and check the memory implications of playing that way.

这篇关于以块形式下载文件(Windows Phone)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆