我正在使用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
答案 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(左右)数组并将其写入隔离存储来一次写入一个块,直到数据用完为止。