1

I want to save a zip file directly to isolated storage from server , But the problem i am facing was when i try to save using the below code , i get out of memory exception since my file size is > 150 MB some times. So i posted a question here and the suggestion was

you can download such a file directly to IsolatedStorage, but if you want to put that into Memory - there can be a problem.

So how can i save a file from server directly to isolated storage without saving to memory . The code i used is posted here

client = new WebClient();
    client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
    client.OpenReadAsync(new Uri(fileurl), objRef);

    private async void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
    {

            var file = IsolatedStorageFile.GetUserStoreForApplication();
            if (!file.DirectoryExists("Folderpath/Files"))
            {
                file.CreateDirectory("Folderpath/Files");
            }
            string hpubFile = "/Folderpath/Files/fileName" ;
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(hpubFile, System.IO.FileMode.OpenOrCreate, FileAccess.ReadWrite, file))
            {
                byte[] buffer = new byte[1024];
                while (e.Result.Read(buffer, 0, buffer.Length) > 0)
                {
                    stream.Write(buffer, 0, buffer.Length);
                }
            }
        }
    }
Community
  • 1
  • 1
Sebastian
  • 4,625
  • 17
  • 76
  • 145
  • You are getting the data from your server and you want to save in your App and that is of big size – Dragon Feb 10 '14 at 08:20
  • 1
    Try setting the `AllowReadStreamBuffering` property of the WebClient to `false`, otherwise it will download the whole file before raising the `OpenReadCompleted` event – Kevin Gosse Feb 10 '14 at 08:39
  • @Dragon Yes exactly ,i want to save the zip from a remote url to isolated storage and size of zip file is not a fixes one . it may vary from 10 Mb to 500 MB – Sebastian Feb 10 '14 at 08:59
  • @JMat @KooKiz is right, first try to use code you already have: http://stackoverflow.com/questions/21572276/downloading-and-saving-a-file-async-in-windows-phone-8/21573527#21573527 , just add in your DownloadFIle, before `OpenReadComleted` wc.AllowReadStreamBuffering = false; In the answer I've posted stream is read async, then only buffer amount should be in memory, – Romasz Feb 10 '14 at 09:17
  • @Romasz i am getting this exception Read is not supported on the main thread when buffering is disabled.If you want i can share full code – Sebastian Feb 10 '14 at 09:50
  • @Romasz Here is the full code i used for downloading according to the link u given http://pastebin.com/HiHwLM6T – Sebastian Feb 10 '14 at 09:59
  • Use linq why you are using Isolated Storage?? or you can use sqlite file . – Dragon Feb 10 '14 at 10:36
  • @Dragon can i use Sqlite for storing a zip file data content? – Sebastian Feb 10 '14 at 10:39

2 Answers2

0

Then maybe try such an approach - do it with HttpWebRequest:

    1. First extend your HttpWebRequest with awaitable GetResponseAsync - WP lacks this method, but with TaskCompletitionSource you can write your own implementation. Somwhere in your Namespace, make a static class:
public static class Extensions
{
    public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest webRequest)
    {
        TaskCompletionSource<HttpWebResponse> taskComplete = new TaskCompletionSource<HttpWebResponse>();
        webRequest.BeginGetResponse(
            asyncResponse =>
            {
                try
                {
                    HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
                    HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
                    taskComplete.TrySetResult(someResponse);
                }
                catch (WebException webExc)
                {
                    HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
                    taskComplete.TrySetResult(failedResponse);
                }
                catch (Exception exc) { taskComplete.SetException(exc); }
            }, webRequest);
        return taskComplete.Task;
    }
}

    2. Then modify your code to use it:
CancellationTokenSource cts = new CancellationTokenSource();
enum Problem { Ok, Cancelled, Other };

public async Task<Problem> DownloadFileFromWeb(Uri uriToDownload, string fileName, CancellationToken cToken)
{
    try
    {
        HttpWebRequest wbr = WebRequest.CreateHttp(uriToDownload);
        wbr.Method = "GET";
        wbr.AllowReadStreamBuffering = false;
        WebClient wbc = new WebClient();

        using (HttpWebResponse response = await wbr.GetResponseAsync())
        {
            using (Stream mystr = response.GetResponseStream())
            {
                using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
                  {
                      if (ISF.FileExists(fileName)) return Problem.Other;
                      using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
                      {
                          const int BUFFER_SIZE = 100 * 1024;
                          byte[] buf = new byte[BUFFER_SIZE];

                          int bytesread = 0;
                          while ((bytesread = await mystr.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
                          {
                              cToken.ThrowIfCancellationRequested();
                              file.Write(buf, 0, bytesread);
                          }
                      }
                  }
                  return Problem.Ok;
              }
          }
      }
      catch (Exception exc)
      {
          if (exc is OperationCanceledException)
              return Problem.Cancelled;
          else return Problem.Other; 
      }
  }

    3. And download:
  private async void Downlaod_Click(object sender, RoutedEventArgs e)
  {
      cts = new CancellationTokenSource();
      Problem fileDownloaded = await DownloadFileFromWeb(new Uri(@"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt", cts.Token);
      switch(fileDownloaded)
      {
          case Problem.Ok:
              MessageBox.Show("Problem with download");
              break;
          case Problem.Cancelled:
              MessageBox.Show("Download cancelled");
              break;
          case Problem.Other:
          default:
              MessageBox.Show("Other problem with download");
              break;
      }
  }

As I've my WebResponse, I get a stream from it and then read it async.
Please be aware that I've not tested the code above, but hopefuly it may give you some ideas how this can work.

Romasz
  • 29,662
  • 13
  • 79
  • 154
  • This is the most stable approach i found till this time and by following this i have been able to download a file of size > 150 MB to isolated storage One more question . Is it possible to track the progress percentage of download operation ( to show user a feedback on how much download is finished) – Sebastian Feb 11 '14 at 06:50
  • @JMat Here http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx you will find all you need. – Romasz Feb 11 '14 at 09:55
  • 1
    Here http://pastebin.com/CsHn938p is the final code i used and i tried to download a file of size 150 MB and i noticed one strange thing the downloading returns OK status @ 64 MB and i retry download then it returns OK status @ 135 MB , again i try to download then only it downloads full size ie 15o MB . Any idea why its like that? @Romasz – Sebastian Feb 12 '14 at 06:26
  • @JMat Have you checked the file size after download? Not this lableProgress.Content. – Romasz Feb 12 '14 at 08:49
  • Yes for small files its working perfectly but for bigger files its not, I am downloading a zip file and i have to unzip it after downloading . If the download is not perfect i will get exceptions while unzipping . I am investigating more on this and not seeing any specific reason for execution being paused during download. – Sebastian Feb 12 '14 at 10:56
  • @JMat I assume that your App doesn't go to background, lock screen, and it doesn't loose connection? – Romasz Feb 12 '14 at 11:26
  • Still not been able to trace the reason exactly. But most of time its stable ( I think problem occurs of unstable network connection only ) – Sebastian Feb 12 '14 at 12:41
  • @JMat maybe await mystr.ReadAsync returns 0 even if it's not the end of the stream. I would check this. If you found something - give me a sign. But according to http://msdn.microsoft.com/pl-pl/library/hh137813(v=vs.110).aspx it should return 0 when EndOfStream occurs. – Romasz Feb 12 '14 at 12:46
0

Yes you can and you can do it like first save original data in Sqlite file and use in the code and then call server for changes which are made in the data and get those updates so you don't have to download data again and again.. Also if you have to download data on each call then you can use sqlite or linq.

Dragon
  • 126
  • 8
  • BUt my requirement is different , i have to download zip file as a file itself and unzip the contents and then save those to isolated storage. Also there is a chance that zip file is modified on server , so i hav to download every time whenever i requested – Sebastian Feb 10 '14 at 11:07
  • So that is i am saying if it is not changing predownload the file and save in a sqlite file in your code open the sqlite file and save data in Isolated storage and every time you open the app maintain a check statement or if there is any change then change it in the Isolated storage otherwise dont download it.This will help you in again and again downloading the data and time . – Dragon Feb 10 '14 at 13:40