c# - 分块下载文件 (Windows Phone)

标签 c# windows-phone-7 download chunking

在我的应用程序中,我可以从 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 事件后将大文件分成 block ,但这只会帮助解决部分问题。 . 首要问题是如何防止下载过程中手机卡顿有没有办法分块下载大文件?然后我可以使用找到的解决方案来通过解密过程。 (而且我仍然需要找到一种方法将如此大的文件加载到 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));
                                }
                            }
                        }
                    }
                }
            }

最佳答案

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

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

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

我编写了一个应用程序,它以 64K block 下载电影(最多 2.6GB),然后使用 MediaPlayerLauncher 从 IsolatedStorage 播放它们.通过 MediaElement 播放应该也可以,但我尚未验证。您可以通过将大文件直接加载到 IsolatedStorage(通过 Isolated Storage Explorer 或类似工具)来测试这一点,并检查以这种方式播放的内存影响。

关于c# - 分块下载文件 (Windows Phone),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14602186/

相关文章:

c# - 根据环境使用具有不同存储帐户的 Azure Functions

c# - 具有通用类型的 Caliburn Micro phonecontainer 注入(inject)服务,始终为 null

c# - 如何执行卡片翻转动画

android - 如何在android中的不同 Activity 中使用带有进度条的异步任务

c# - 将查询转换为 Lambda 表达式

c# - 如何动态组合 linq 查询?

c# - EventLog WriteEntry 不写入指定日志,而是写入应用程序日志

c# - Silverlight:奇怪的数据绑定(bind)问题

r - downloadButton/downloadHandler无法识别文件名参数

grails - 通过具有部分下载支持的 Controller 提供文件