(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Connecting Windows 8 applications with services: Part 3: Background transfers in Windows 8

(3 votes)
Gill Cleeren
>
Gill Cleeren
Joined Apr 02, 2010
Articles:   63
Comments:   6
More Articles
2 comments   /   posted on Sep 18, 2012
Categories:   General
Tweet

The focus of this series is giving you an overview of how Windows 8 applications can connect with the outside world. In the two previous parts, we’ve seen a number of service types that Windows 8 apps can connect with. Among others, we’ve seen how to communicate with WCF services, oData services, RSS endpoints and quite a few more. Further in this series, we are going to be exploring some more exotic types of services, including sockets.

Before we go that way though, we are going to take a look at some practical uses of (service) communication. In this part, we’re going to see how an application can download and upload files in the background using background transfers.

Background transfers in Windows 8

Windows 8 breaks with the “multiple window paradigm”: instead, only one application can be the foreground application (unless we are using SnapView where 2 apps are placed next to each other). This ensures that the system is focusing on that one foreground application. The idea here is that all system resources are available for that one application which will result in a great performance. These resources include CPU, disk and network. When an app is moved to the background, it no longer gets to execute any code: it has no access to CPU, disk or network. The memory image is kept aside while the application is suspended. Later on, the application might get resumed. In that case, Windows will again schedule the app to have CPU time available for it. A background process API is available; the key aspect here is that the code that will run in the background is not the same code as the foreground app. It’s a different DLL that will be scheduled to run based on a system of triggers (a TimeTrigger being one of the most important ones).

This new process lifecycle management brings with it several implications we never had to deal with in our desktop development days. Take for example an application that needs to download or upload files. The actual downloading (or uploading) of large chunks of textual data or binary data (for example an image) from within a Metro application requires the application to remain the foreground app throughout the transfer process. This would be the case if we would use a WCF service or use the HttpClient. Should the user suspend the application, the download or upload would be stopped/paused and potentially fail (think of all the implications you’ll encounter if you need to start working with partial downloads and the resuming of these).

Microsoft thought of this implication and built into WinRT the BackgroundDownloader class. This class, which lives in the Windows.Networking.BackgroundTransfer namespace allows us to create downloads, not executed in the application itself but in a separate process. This process, the BackgroundTransferHost.exe, is managing itself entirely independently from the application. This effectively means that if the app needs to download a file, it will register this with the BackgroundDownloader which will result in the BackgroundTransferHost.exe to actually execute the download. If the main app becomes suspended, the download will continue and the file will be created where the app requested it to be in the first place. Let’s take a look at using the BackgroundDownloader class in a sample.

Downloading files in the background

Create a Windows 8 app and name it MyBackgroundDownloader. The UI for the app is pretty simple: it allows the user to enter a URL to download (in this case, we will be saving videos) and a file name to identify the file locally.

<StackPanel Margin="25" HorizontalAlignment="Left" >
    <TextBlock Text="The Video 8 Download tool" FontSize="42"></TextBlock>
    <TextBlock Text="Enter the file URL you want to download" FontSize="18"></TextBlock>
    <TextBox Width="300" Height="20" Name="UrlTextBox" HorizontalAlignment="Left"></TextBox>
    <TextBlock Text="Enter the filename" FontSize="18"></TextBlock>
    <TextBox Width="300" Height="20" Name="FileNameTextBox" HorizontalAlignment="Left"></TextBox>
    <StackPanel>
        <Button Content="Start downloading" Name="StartDownloadingButton" 
                Click="StartDownloadingButton_Click_1">
        </Button>
         <Button Content="Cancel downloading" Name="CancelDownloadingButton" 
                 Click="CancelDownloadingButton_Click_1">
         </Button>
    </StackPanel>
    <TextBlock Name="StatusTextBlock" FontSize="20" Foreground="LightGray" Text="0%"></TextBlock>
 </StackPanel>

The application supports downloading and cancelling a download. When clicking on the StartDownloadingButton, we are going to initiate the download of the file as follows. First, we create a local StorageFile where we will store the downloaded file. Secondly, we instantiate a BackgroundDownloader and use the CreateDownload() to create a DownloadOperation instance. At this point, nothing is downloading yet.

private async void StartDownloadingButton_Click_1(object sender, RoutedEventArgs e)
{
    StorageFile storageFile = 
        await KnownFolders.VideosLibrary.CreateFileAsync(
            FileNameTextBox.Text, 
            CreationCollisionOption.GenerateUniqueName);
    BackgroundDownloader downloader = new BackgroundDownloader();
    DownloadOperation download = downloader.CreateDownload(new Uri(UrlTextBox.Text), storageFile);
    await PerformDownload(download);
}

In the PerformDownload(), we will start the downloading using the StartAsync(). This method will effectively start the downloading of the file, starting the BackgroundTransferHost.exe process. Using the AsTask() extension method, we pass a CancellationTokenSource (more later) and a callback for reporting progress.

private async void PerformDownload(DownloadOperation download)
{
    Progress<DownloadOperation> downloadProgressCallback = 
        new Progress<DownloadOperation>(UpdateDownloadProgress);
    await download.StartAsync().AsTask(cancellationTokenSource.Token, downloadProgressCallback);
}

In the UpdateDownloadProgress() callback, we need to check how far the download is. We can do this by comparing the amount of downloaded data to the total size of the file. When we want to update this value in the UI (in this case in the StatusTextBlock), we need to marshal to the UI thread using the RunAsync() of the Dispatcher.

private void UpdateDownloadProgress(DownloadOperation download)
{
    double percent = 100;
    if (download.Progress.TotalBytesToReceive > 0)
    {
        percent = download.Progress.BytesReceived * 100 / download.Progress.TotalBytesToReceive;
    }
    this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        StatusTextBlock.Text = percent + "%";
    });
}

When the user clicks on the Cancel button, we want to cancel the download. This can be done using the following code:

private void CancelDownloadingButton_Click_1(object sender, RoutedEventArgs e)
{
    cancellationTokenSource.Cancel();
}

All downloads (should we have more registered) that are associated with the CancellationTokenSource are cancelled.

If we run the application now and enter the URL for a video as well as a local file name, we see that the download will start.

The file is physically created on the file system in the Video library.

If we open the Task manager (and therefore suspending the application), we can see that a second process is created: MyBackgroundDownloader Download/Upload host.

If we open the associated process, we can see that indeed the BackgroundTransferHost process is running and performing our download.

If we cancel the download now, we’ll get an error telling us that we aren’t handing the Task being cancelled.

This can easily be solved by changing the code of the PerformDownload as follows:

private async Task PerformDownload(DownloadOperation download)
{
    Progress<DownloadOperation> downloadProgressCallback = 
             new Progress<DownloadOperation>(UpdateDownloadProgress);
    try
    {
        await download.StartAsync().AsTask(cancellationTokenSource.Token, downloadProgressCallback);
    }
    catch (TaskCanceledException ex)
    {
        StatusTextBlock.Text = "Cancelled";
    }
}

Uploading files in the background

Let’s now extend the app and add uploading functionality as well. The story is of course the same: by registering uploads with the BackgroundTransferHost, we can suspend the app and let it do the upload in the background. Since the code is similar too, we’ll go over it a bit quicker.

To test uploading in the background, we are using a new application called the PictureUploader. The interface is very simple: a button opens a FilePicker that allows the user to select an image (only jpg’s are allowed because of the filter we’ll apply on the FilePicker). After selecting the image, the user can click to upload to a server-side page. The app is shown below.

In the click event of the image selection button, the following code opens a FilePicker. After successfully selecting a *.jpg file, the Upload button becomes enabled. Failure to do so displays an error to the user.

private Uri serverAddress;
private CancellationTokenSource cancellationTokenSource;
private StorageFile selectedFile;
private async void SelectImageButton_Click_1(object sender, RoutedEventArgs e)
{
    var filePicker = new Windows.Storage.Pickers.FileOpenPicker();
    filePicker.FileTypeFilter.Add(".jpg");
    selectedFile = await filePicker.PickSingleFileAsync();
    if (selectedFile != null)
    {
        UploadImageButton.IsEnabled = true;
    }
    else
    {
        StatusTextBlock.Text = "No image selected";
        return;
    }
}

With the image selected and the selectedFile now representing the file selected by the user, we can now start the upload of the file. For the server-side, I’ve copied a file from the Windows 8 SDK samples (Upload.aspx) which accepts files being uploaded. For your convenience, the code is shown below.

<%@ Page Language="C#" AutoEventWireup="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        if (Request.Headers["Filename"] != null)
        {
            // Simple upload scenario.
            string fileName = Request.Headers["Filename"];
            Response.Write("Filename is " + fileName);
            string saveLocation = Server.MapPath("Data") + "\\" + fileName;
            using (System.IO.FileStream fs = 
                          new System.IO.FileStream(saveLocation, System.IO.FileMode.Create))
            {
                Request.InputStream.CopyTo(fs);
            }
        }
        else
        {
            string formData = Request.Form["FormData"];
            for (int i = 0; i < Request.Files.Count; i++)
            {
                var file = Request.Files[i];
                if (file.ContentLength > 0)
                {
                    string fileName = System.IO.Path.GetFileName(file.FileName);
                    string saveLocation = Server.MapPath("Data") + "\\" + fileName;
                    file.SaveAs(saveLocation);
                }
            }
        }
    }
    catch (Exception ex)
    {
        Trace.Write(ex.Message);
        Response.StatusCode = 500;
        Response.StatusDescription = ex.Message;
        Response.End();
    }
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Upload</title>
</head>
<body>
    Hello
</body>
</html>

When clicking the Upload button, a BackgroundUploader instance is created. Using the CreateUpload() method, we create an UploadOperation. At this point, the upload hasn’t started yet. We now call on to the PerformUpload() method.

private async void UploadImageButton_Click_1(object sender, RoutedEventArgs e)
{
    BackgroundUploader uploader = new BackgroundUploader();
    uploader.SetRequestHeader("Filename", selectedFile.Name);
    UploadOperation upload = uploader.CreateUpload(serverAddress, selectedFile);
    string response = await PerformUpload(upload);
    StatusTextBlock.Text = string.Format("All done! Status: {0}", response);
}

In the PerformUpload() method, we first define the progress callback named uploadProgressCallback. Secondly, we start the upload process through the StartAsync() method of the UploadOperation class. This is again asynchronous so we can await the call. By using the AsTask() extension method, we can pass the callback as well as a cancellation token. The GetResponseInformation() method allows us to get back information about the status of the upload.

private async Task<string> PerformUpload(UploadOperation upload)
{
    Progress<UploadOperation> uploadProgressCallback = 
          new Progress<UploadOperation>(UpdateUploadProgress);
    await upload.StartAsync().AsTask(cancellationTokenSource.Token, uploadProgressCallback);
    ResponseInformation response = upload.GetResponseInformation();
    return response.StatusCode.ToString();
}

Finally, the code for the progress of the upload is shown below.

private void UpdateUploadProgress(UploadOperation operation)
{
    double percent = 100;
    if (operation.Progress.TotalBytesToReceive > 0)
    {
        percent = operation.Progress.BytesReceived * 100 / operation.Progress.TotalBytesToReceive;
    }
    this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        StatusTextBlock.Text = percent + "%";
    });
}

Summary

In this article, we’ve seen how we can create apps that upload and download files. While it’s possible to perform file transfers in the foreground using traditional methods, it’s much better to use the built-in classes in WinRT, the BackgroundUploader and the BackgroundDownloader, to actually perform the transfers in the background.

In the next article, we’ll be diving into tile updates and push notifications from services. Stay tuned!

About the author

Gill Cleeren is Microsoft Regional Director (www.theregion.com), Silverlight MVP (former ASP.NET MVP) and Telerik MVP. He lives in Belgium where he works as .NET architect at Ordina (http://www.ordina.be/). Passionate about .NET, he’s always playing with the newest bits. In his role as Regional Director, Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF at conferences including TechEd Berlin 2010, TechDays Belgium – Switzerland - Sweden, DevDays NL, NDC Oslo Norway, SQL Server Saturday Switserland, Spring Conference UK, Silverlight Roadshow in Sweden, Telerik RoadShow UK… He’s also the author of many articles in various developer magazines and for SilverlightShow.net and he organizes the yearly Community Day event in Belgium. He also leads Visug (www.visug.be), the largest .NET user group in Belgium. Gill is the author of “Silverlight 4 Data and Services Cookbook”. In 2012, the second edition, “Silverlight 5 Data and Services Cookbook” is released.

You can find his blog at www.snowball.be.

Twitter: @gillcleeren


Subscribe

Comments

  • emulemaster

    Re: Connecting Windows 8 applications with services: Part 3: Background transfers in Windows 8


    posted by emulemaster on Jan 26, 2013 20:59

    Hi Gill,

    What about an example of these in an app that uses MVVM? I'm already stuck with this....

  • Little_Hendrix

    Re: Connecting Windows 8 applications with services: Part 3: Background transfers in Windows 8


    posted by Little_Hendrix on Feb 01, 2013 20:41

    Hi, I have some problems during the upload. I got Status 200, but when I check the server there's no file uploaded..I'm using apache server with your scripts

Add Comment

Login to comment:
  *      *       

From this series