4
votes

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!

3
On Windows Phone 8 the WebClient supports Task-Based asynchronous programming (see the TaskAsync suffixed methods). However, the HttpClient Class that you can get it from the Microsoft HTTP Client Libraries 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

3 Answers

3
votes

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.

3
votes

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]

-1
votes

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));