3

I would like to download a xml file from web, then save it to the local storage but I do not know how to do that. Please to help me clearly or give me an example. Thank you.

Carson Vo
  • 476
  • 6
  • 20

1 Answers1

5

Downloading a file is a huge subject and can be done in many ways. I assume that you know the Uri of the file you want to download, and want you mean by local is IsolatedStorage.

I'll show three examples how it can be done (there are also other ways).

1. The simpliest example will dowload string via WebClient:

public static void DownloadFileVerySimle(Uri fileAdress, string fileName)
{
   WebClient client = new WebClient();
   client.DownloadStringCompleted += (s, ev) =>
   {
       using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
       using (StreamWriter writeToFile = new StreamWriter(ISF.CreateFile(fileName)))
           writeToFile.Write(ev.Result);
   };
   client.DownloadStringAsync(fileAdress);
}

As you can see I'm directly downloading string (ev.Result is a string - that is a disadventage of this method) to IsolatedStorage. And usage - for example after Button click:

private void Download_Click(object sender, RoutedEventArgs e)
{
   DownloadFileVerySimle(new Uri(@"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
}

2. In the second method (simple but more complicated) I'll use again WebClient and I'll need to do it asynchronously (if you are new to this I would suggest to read MSDN, async-await on Stephen Cleary blog and maybe some tutorials).

First I need Task which will download a Stream from web:

public static Task<Stream> DownloadStream(Uri url)
{
   TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>();
   WebClient wbc = new WebClient();
   wbc.OpenReadCompleted += (s, e) =>
   {
      if (e.Error != null) tcs.TrySetException(e.Error);
      else if (e.Cancelled) tcs.TrySetCanceled();
      else tcs.TrySetResult(e.Result);
   };
   wbc.OpenReadAsync(url);
   return tcs.Task;
}

Then I'll write my method downloading a file - it also need to be async as I'll use await DownloadStream:

public enum DownloadStatus { Ok, Error };

public static async Task<DownloadStatus> DownloadFileSimle(Uri fileAdress, string fileName)
{
   try
   {
       using (Stream resopnse = await DownloadStream(new Uri(@"http://filedress/myfile.txt", UriKind.Absolute)))
         using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
         {
            if (ISF.FileExists(fileName)) return DownloadStatus.Error;
            using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
                resopnse.CopyTo(file, 1024);
            return DownloadStatus.Ok;
         }
   }
   catch { return DownloadStatus.Error; }
}

And usage of my method for example after Button click:

private async void Downlaod_Click(object sender, RoutedEventArgs e)
{
   DownloadStatus fileDownloaded = await DownloadFileSimle(new Uri(@"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
   switch (fileDownloaded)
   {
       case DownloadStatus.Ok:
            MessageBox.Show("File downloaded!");
            break;
       case DownloadStatus.Error:
       default:
            MessageBox.Show("There was an error while downloading.");
            break;
    }
}

This method can have problems for example if you try to download very big file (example 150 Mb).

3. The third method - uses WebRequest with again async-await, but this method can be changed to download files via buffer, and therefore not to use too much memory:

First I'll need to extend my Webrequest by a method that will asynchronously return a Stream:

public static class Extensions
{
    public static Task<Stream> GetRequestStreamAsync(this WebRequest webRequest)
    {
        TaskCompletionSource<Stream> taskComplete = new TaskCompletionSource<Stream>();
        webRequest.BeginGetRequestStream(arg =>
        {
            try
            {
                Stream requestStream = webRequest.EndGetRequestStream(arg);
                taskComplete.TrySetResult(requestStream);
            }
            catch (Exception ex) { taskComplete.SetException(ex); }
        }, webRequest);
        return taskComplete.Task;
    }
}

Then I can get to work and write my Downloading method:

public static async Task<DownloadStatus> DownloadFile(Uri fileAdress, string fileName)
{
   try
   {
      WebRequest request = WebRequest.Create(fileAdress);
      if (request != null)
      {
          using (Stream resopnse = await request.GetRequestStreamAsync())
          {
             using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
             {
                  if (ISF.FileExists(fileName)) return DownloadStatus.Error;
                    using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
                    {
                       const int BUFFER_SIZE = 10 * 1024;
                       byte[] buf = new byte[BUFFER_SIZE];

                       int bytesread = 0;
                       while ((bytesread = await resopnse.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
                                file.Write(buf, 0, bytesread);
                   }
             }
             return DownloadStatus.Ok;
         }
     }
     return DownloadStatus.Error;
  }
  catch { return DownloadStatus.Error; }
}

Again usage:

private async void Downlaod_Click(object sender, RoutedEventArgs e)
{
   DownloadStatus fileDownloaded = await DownloadFile(new Uri(@"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
   switch (fileDownloaded)
   {
       case DownloadStatus.Ok:
           MessageBox.Show("File downloaded!");
           break;
       case DownloadStatus.Error:
       default:
           MessageBox.Show("There was an error while downloading.");
           break;
   }
}

Those methods of course can be improved but I think this can give you an overview how it can look like. The main disadvantage of these methods may be that they work in foreground, which means that when you exit your App or hit start button, downloading stops. If you need to download in background you can use Background File Transfers - but that is other story.

As you can see you can reach your goal in many ways. You can read more about those methods on many pages, tutorials and blogs, compare an choose the most suitable.

Hope this helps. Happy coding and good luck.

Romasz
  • 29,662
  • 13
  • 79
  • 154
  • Thanks Romasz. Can you show me where is the IsolatedStorage folder in my computer after I save this file. – Carson Vo Feb 16 '14 at 16:27
  • 2
    @Carson IsolatedStorage folder is in your phone, that's the 'place' strictly connected to your App and no other app has acess to it. However you can surely explore your developer device (or emulator) by [IsolatedStorage Explorer](http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh286408(v=vs.105).aspx) or by [IsolatedStorage Spy](http://isostorespy.codeplex.com/) for example. – Romasz Feb 16 '14 at 18:38
  • @Carson You are welcome. See also that I've mentioned (after next edit) about Background downloading - it may be sometimes useful. Happy coding – Romasz Feb 17 '14 at 07:07
  • your first method is very easy to download xml file. But my xlm file in web is more 900kb and xml file in isoStore is 400kb. I can't work with this xlm. My xml file in web: http://1.storeofcarson.appspot.com/res/NetFlixTop50.xml – Carson Vo Feb 17 '14 at 09:00
  • 2
    @Carson Then you have found the next disadventage of downloading a string ;) - it is the fact that string can have different Encoding. To solve your issue you probably need only to change the encoding of streamwriter, the line below should fix your filesize: using (StreamWriter writeToFile = new StreamWriter(ISF.CreateFile(fileName), Encoding.Unicode)) – Romasz Feb 17 '14 at 09:26
  • 1
    I would like to thank you a thousands times. Every my problems are solved. – Carson Vo Feb 17 '14 at 09:45
  • Dear Romasz, Can I check the size of the file to download. Because I don't want my user download again my file which was not updated. It will use of my "Outgoing Bandwidth". – Carson Vo Feb 28 '14 at 07:06
  • @Carson Sorry for late answer, but I was on holiday. Answer to your question depends on the method you are using. If you are using Stream - you can try to check Stream.Length. But please keep in mind that file length doesn't guarantee that the file wasn't updated. If it's your server with the file (and if it's a big file), maybe consider making a small file with some 'time of the update' information. – Romasz Mar 02 '14 at 18:18