4

How can I download and save a file to IsolatedStorage asynchronously? I used WebClient first for the purpose, but I couldn't await till completion so I found some useful code here.

However, this is also not in complete format. I found the function:

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

But how can I save this file to IsolatedStorage? Someone help me to complete this functionality!

Community
  • 1
  • 1
Sebastian
  • 4,625
  • 17
  • 76
  • 145
  • On Windows Phone 8 the `WebClient` supports Task-Based asynchronous programming (see the **TaskAsync** suffixed methods). However, the [HttpClient Class](http://msdn.microsoft.com/library/system.net.http.httpclient.aspx) that you can get it from the [Microsoft HTTP Client Libraries](https://www.nuget.org/packages/Microsoft.Net.Http) **NuGet** package is better suited for asynchronous programming. Your code will become a lot cleaner. `Task` returning asynchronous methods should also be suffixed **Async** (exception for `WebClient` that already had **Async** suffixed methods). – Paulo Morgado Feb 07 '14 at 00:22

3 Answers3

3

Try something like this (not tested) :

try
{
    using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
    {
        IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile("fileNameHere");

        BitmapImage bitmap = new BitmapImage();
        var stream = await DownloadFile(new Uri("http://someuri.com", UriKind.Absolute));
        bitmap.SetSource(stream);
        WriteableBitmap wb = new WriteableBitmap(bitmap);

        // Encode WriteableBitmap object to a JPEG stream.
        Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
        fileStream.Close();
    }
}
catch (Exception ex)
{
    //Exception handle appropriately for your app  
}

[Reference]

Community
  • 1
  • 1
har07
  • 88,338
  • 12
  • 84
  • 137
3

This, what har07 posted, should work. In case you need to extend your method a little, I would try to do something like this (though it is not tested, but should work):

(Rebuilt after comment - with Cancellation)

// first define Cancellation Token Source - I've made it global so that CancelButton has acces to it
CancellationTokenSource cts = new CancellationTokenSource();
enum Problem { Ok, Cancelled, Other }; // results of my Task

// cancelling button event
private void CancellButton_Click(object sender, RoutedEventArgs e)
{
   if (this.cts != null)
        this.cts.Cancel();
}

// the main method - I've described it a little below in the text
public async Task<Problem> DownloadFileFromWeb(Uri uriToDownload, string fileName, CancellationToken cToken)
{
   try
   {
       using (Stream mystr = await DownloadFile(uriToDownload))
           using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
           {
           if (ISF.FileExists(fileName)) return Problem.Other;
           using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
           {
               const int BUFFER_SIZE = 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; 
    }
}

// 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("File downloaded");
           break;
      case Problem.Cancelled:
           MessageBox.Show("Download cancelled");
           break;
      case Problem.Other:
      default:
           MessageBox.Show("Other problem with download");
           break;
    }
}

I've added Cancellation Token - it means that your download operation can be cancelled after Button.Click. On the other hand if await DownloadFile(uriToDownload) is Cancelled it automatically throws OperationCancelled - then you catch that exception and return adequate result.

I haven't run that code, but it may show the main idea.

Community
  • 1
  • 1
Romasz
  • 29,662
  • 13
  • 79
  • 154
  • How can we handle Canceling of webclient download in this case.I set if (e.Cancelled) tcs.TrySetCanceled();. BUt how can i check it on main thread – Sebastian Feb 05 '14 at 11:19
  • @JMat I've edited my answer and tried to add Cancellation ability. There can be some mistakes (I haven't run it) - but hopefully you get the main idea. – Romasz Feb 05 '14 at 12:19
  • Yes it worked and i am struggling with a strange problem while downloading file OutOf Memory exception .If you have some suggestions please comment to post http://stackoverflow.com/questions/21594417/outofmemoryexception-while-downloading-files-wp8 – Sebastian Feb 06 '14 at 05:09
-1

In my opinion best way to use native implementation like this (Wrapping Begin/End asynchronous API):

var url = new Uri(UriString, UriKind.Absolute);
var fileName = Path.GetFileName(url.LocalPath);

var w = WebRequest.CreateHttp(url);
var response = await Task.Factory.FromAsync<WebResponse>(w.BeginGetResponse, w.EndGetResponse, null);
await response.GetResponseStream().CopyToAsync(new FileStream(ApplicationData.Current.LocalFolder.Path + @"\" + fileName, FileMode.CreateNew));
Astemir Almov
  • 396
  • 2
  • 16
  • In this question is a duplicate of the one you've linked, please flag it as a duplicate instead of leaving an answer to that effect. If it is not a duplicate, please leave a complete answer instead of a link-only answer. – josliber Sep 25 '15 at 13:13