使用WebClient在WP7中读取和保存大文件

时间:2012-02-24 18:57:03

标签: c# .net windows-phone-7

我正在使用silverlight在wp7中开发一个音乐播放器。我的终端用户在漫游时没有良好的互联网连接。我想给他们一个在手机中下载音乐的选项。 为此,我写了一个下载管理器,他们可以使用它来连接wifi时下载音乐。 我觉得我面临的是,我的音乐存储在我的服务器上,我正在使用WebCLient类在手机上下载音乐。 如果文件大小超过68 MB,我的应用程序将获得OutOfMemory异常。 这是代码:

public void DownloadKirtan(KirtanViewModel kirtanVm,bool QueueItem = true)         {

        {
            if (kirtanVm.LocationPath != null)
            {
                WebClient webClient = new WebClient();
                //webClient.AllowWriteStreamBuffering = false;
               // webClient.AllowReadStreamBuffering = false;
                if (QueueItem == false)
                {
                    //App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload[kirtanVm] = webClient;
                   // App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Add(kirtanVm//
                    webClient = App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload[kirtanVm];
                    kirtanVm.IsDownloadedForOfflineViewing = "Started";
                    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
                    webClient.OpenReadAsync(kirtanVm.LocationPath, kirtanVm);
                }
                else if (!App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.ContainsKey(kirtanVm))
                {
                    App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Add(kirtanVm, webClient);
                    kirtanVm.IsDownloadedForOfflineViewing = "Started";
                    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
                    webClient.OpenReadAsync(kirtanVm.LocationPath, kirtanVm);


                }
                // webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);

            }
        }
    }

void webClient_OpenReadCompleted(object sender,OpenReadCompletedEventArgs e)         {

        KirtanViewModel kirtanVm = e.UserState as KirtanViewModel;
        try
        {
            if (e.Cancelled == false)
            {
                if (e.Result != null)
                {

                    ((WebClient)sender).OpenReadCompleted -= webClient_OpenReadCompleted;

                    #region Isolated Storage Copy Code


                    IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication();



                    bool checkQuotaIncrease =  IncreaseIsolatedStorageSpace(e.Result.Length);


                    if (checkQuotaIncrease)
                    {

                        string VideoFile = "";
                        VideoFile = GetUrlOfOfflineContent(kirtanVm);

                        using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(VideoFile, FileMode.Create, isolatedStorageFile))
                        {
                            long VideoFileLength = (long)e.Result.Length;


                            byte[] byteImage = new byte[VideoFileLength];

                            e.Result.Read(byteImage, 0, byteImage.Length);

                                isolatedStorageFileStream.Write(byteImage, 0, byteImage.Length);


                                        kirtanVm.IsDownloadedForOfflineViewing = "True";
                                        kirtanVm.DownloadSize = "Size=" + (VideoFileLength / 1000000).ToString() + " MB";
                                        kirtanVm.DownloadProgress = "100%";
                                        Settings.OfflineKirtanContents.Value.Add(kirtanVm);
                                        AddRemoveKirtanInOfflineModels(kirtanVm, true);





                        }

                    #endregion
                    }
                    else
                    {
                        kirtanVm.IsDownloadedForOfflineViewing = "False";
                        App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
                        MessageBox.Show("There is not enough space in your phone to store this media. You need " + e.Result.Length + " bytes of storage.Please free some offline contents you have downloaded by going to Offline content managemnt screen in Settings Section and try again or increase the storage on your phone.");
                    }



                    // mediaFile.SetSource(isolatedStorageFileStream);

                    // mediaFile.Play();

                    // progressMedia.Visibility = Visibility.Collapsed;





                  }
            }

        }

        catch (Exception ex)
        {
            kirtanVm.IsDownloadedForOfflineViewing = "Failed";

            //App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
           MessageBox.Show(ex.ToString());

        }

    }

我遇到的问题是当我得到e.result这是一个流对象时。要写入isolatedStoreFileStream,我必须再次读取它到字节数组,然后将其保存到isolatedstoragefile。这是低效的,它消耗双倍内存。内存为60 MB(对于60MB文件)由WebClient e.result流对象然后60 MB转换为数组。所以内存消耗是128 MB。是否有更好的方法来下载大文件并将其存储到IsolatedStorage>

更新:我现在使用chunck大小使用以下代码,而不是在内存中读取所有内容但我仍然在100 MB的大文件上出现内存错误

void webClient_OpenReadCompleted(object sender,OpenReadCompletedEventArgs e)         {

        KirtanViewModel kirtanVm = e.UserState as KirtanViewModel;
        try
        {
            if (e.Cancelled == false)
            {
                if (e.Result != null)
                {

                    ((WebClient)sender).OpenReadCompleted -= webClient_OpenReadCompleted;

                    #region Isolated Storage Copy Code
                    bool checkQuotaIncrease =  IncreaseIsolatedStorageSpace(e.Result.Length);
                    if (checkQuotaIncrease)
                    {

                        string VideoFile = "";
                        VideoFile = GetUrlOfOfflineContent(kirtanVm);
                        ThreadPool.QueueUserWorkItem( k =>{
                        using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                        {
                            using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(VideoFile, FileMode.Create, isolatedStorageFile))
                            {
                                long VideoFileLength = (long)e.Result.Length;


                                using (BinaryWriter writer = new BinaryWriter(isolatedStorageFileStream))
                                {
                                    Stream resourceStream = e.Result;//streamResourceInfo.Stream;
                                    long length = resourceStream.Length;
                                    byte[] buffer = new byte[32];
                                    int readCount = 0;
                                    using (BinaryReader reader = new BinaryReader(resourceStream))
                                    {                    // read file in chunks in order to reduce memory consumption and increase performance                    
                                        while (readCount < length)
                                        {
                                            int actual = reader.Read(buffer, 0, buffer.Length);
                                            readCount += actual;
                                            writer.Write(buffer, 0, actual);
                                        }
                                    }
                                }
                                 kirtanVm.IsDownloadedForOfflineViewing = "True";
                                kirtanVm.DownloadSize = "Size=" + (VideoFileLength / 1000000).ToString() + " MB";
                                kirtanVm.DownloadProgress = "100%";
                                Settings.OfflineKirtanContents.Value.Add(kirtanVm);
                                AddRemoveKirtanInOfflineModels(kirtanVm, true);
                            }
                        }
                       });

                                //byte[] byteImage = new byte[VideoFileLength];

                                //e.Result.Read(byteImage, 0, byteImage.Length);
                                // e.re
                                //ThreadPool.QueueUserWorkItem( k =>{
                                //     isolatedStorageFileStream.Write(byteImage, 0, byteImage.Length);
                                //isolatedStorageFileStream.Close();
                                //Application.Current.RootVisual.Dispatcher.BeginInvoke( ()=>
                                //    {
                                //        kirtanVm.IsDownloadedForOfflineViewing = "True";
                                //        kirtanVm.DownloadSize = "Size=" + (VideoFileLength / 1000000).ToString() + " MB";
                                //        kirtanVm.DownloadProgress = "100%";
                                //        Settings.OfflineKirtanContents.Value.Add(kirtanVm);
                                //        AddRemoveKirtanInOfflineModels(kirtanVm, true);
                                //    });

                                //});
                                //StreamWriter writer=new StreamWriter(isolatedStorageFileStream);
                                // writer.Write(e.Result);
                                // writer.Close();
                                // isolatedStorageFileStream.Write(
                                // e.Result.Write(

                              //  isolatedStorageFileStream.Write(byteImage, 0, byteImage.Length);
                                // isolatedStorageFileStream.Close();




                            }


                    #endregion

                    else
                    {
                        kirtanVm.IsDownloadedForOfflineViewing = "False";
                        App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
                        MessageBox.Show("There is not enough space in your phone to store this media. You need " + e.Result.Length + " bytes of storage.Please free some offline contents you have downloaded by going to Offline content managemnt screen in Settings Section and try again or increase the storage on your phone.");
                    }

                }
            }
            else
            {
                lock (App.ViewModel.LockForCancelForCurrentOfflineDownload)
                {
                    if (App.ViewModel.cancelInitiatedByUserForCurrentOfflineDownload)
                    {
                        kirtanVm.IsDownloadedForOfflineViewing = "False";
                       // kirtanVm.IsOfflineDownloadCancelled = false;
                        App.ViewModel.cancelInitiatedByUserForCurrentOfflineDownload = false;
                    }
                    else
                    {
                        if (kirtanVm.IsDownloadedForOfflineViewing == "Started")// don't queue things again
                        {
                            kirtanVm.IsDownloadedForOfflineViewing = "Failed";
                            // bool ItemExist = App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Any(k => k.Key.Kirtan.KirtanId == kirtanVm.Kirtan.KirtanId);

                            DownloadKirtan(kirtanVm, false);
                        }
                    }
                }
            }



        }

        catch (Exception ex)
        {
            kirtanVm.IsDownloadedForOfflineViewing = "Failed";

            //App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
           MessageBox.Show(ex.ToString());

        }

    }

由于 Verinder

2 个答案:

答案 0 :(得分:1)

最后我找到了解决方案

在我分享的上述代码中打开webClient.AllowReadStreamBuffering = false;,然后再进入 void webClient_OpenReadCompleted(object sender,OpenReadCompletedEventArgs e){} method ..删除对e.Result.Length的所有引用。当不使用缓冲解决方案时,您无法提前知道流大小

long length = resourceStream.Length;                                     
byte[] buffer = new byte[1024];                                     
int readCount = 0;                                     
using (BinaryReader reader = new BinaryReader(resourceStream))                                     {                    // read file in chunks in order to reduce memory consumption and increase performance                                                             
while (true)                                         
{       
int actual = reader.Read(buffer, 0, 1024);   
 if(actual==0)
{
 break;
}
else
   {                                      
 readCount += actual;                                             
writer.Write(buffer, 0, actual);  
}                                       
}                                     
}

除非您已完成阅读,否则一次以1024字节继续读取流。这样,WebClient不会将整个文件缓冲到内存中,并且手机上的最大内存使用量将一次为1KB。你可以下载像这样的Gigs数据。如果要更快地读取,请将缓冲区大小增加到1MB或3 MB。 确保在ThreadPool中运行此代码而不是用户线程

答案 1 :(得分:0)

您需要通过读入4KB(左右)数组并将其写入隔离存储来一次写入一个块,直到数据用完为止。