How to download a file (HTML)
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
This topic will show you how to download a file.
Apps can use the APIs discussed in this topic to enable their app to interact with web services to consume or share popular media formats like photos, music, and video.
When developing an app in JavaScript there are two primary options for requesting files from locations on the Internet. Small files, like site assets that are retrieved frequently, can be downloaded using XHR to make the asynchronous HTTP GET request. This function wraps an XMLHttpRequest call in a promise, the programming pattern that enables asynchronous behavior in JavaScript.
Alternatively, to provide a consistent experience when downloading larger media (video and music) with an operational lifetime that may span beyond multiple app suspensions and/or changes in network availability, your app can use Background Transfer. For a high-level look at Background Transfer, see Transferring data in the background.
Prerequisites
For general help creating a JavaScript app, see Create your first Windows Runtime app using JavaScript. Additionally, JavaScript promises are used in this topic to complete asynchronous operations. For more information on this programming pattern, see Asynchronous programming in JavaScript using promises.
To ensure your app is network ready, you must set the capability in the project Package.appxmanifest file. For a definition of each network capability, see How to configure network isolation capabilities.
All Background Transfer examples in this topic are based on the Background Transfer sample.
Downloading a file using XHR
To initiate a basic asynchronous HTTP request using JavaScript, call XHR and supply it with relevant request data using the Option parameter. By default this method call is a GET request, so the only required values supplied via Option are the URL and responseType. However, many web services require authentication, and these credentials should be included when calling XHR to request resources from any secure web service.
An XHR GET operation also requires that the type of content expected in the response is specified using responseType in the Option parameter. In our example we requested a .png file, so our responseType value is "blob". For a complete list of supported content types and examples of how to request them, see How to download a file with WinJS xhr.
WinJS.xhr({ url: "https://www.microsoft.com/windows/Framework/images/win_logo.png", responseType: "blob" })
.done(
function (request) {
var imageBlob = URL.createObjectURL(request.response);
var imageTag = xhrDiv.appendChild(document.createElement("image"));
imageTag.src = imageBlob;
});
In JavaScript, every promise has two functions that you can use to handle the results of an asynchronous operation: then and done. Both functions take three parameters: a function that's called when the download is complete (that is, when readyState is 4), a function that's called when there's an error, and a function that's called while the download is in progress (when readyState is 2 or 3). Only the done function throws an exception if an error is not handled. This function is preferred when an error function isn't provided.
Downloading a file using Background Transfer
When using Background Transfer, each download exists as a DownloadOperation that exposes a number of control methods used to pause, resume, restart, and cancel the operation. App events (e.g. suspension or termination) and connectivity changes are handled automatically by the system per DownloadOperation; downloads will continue during app suspension periods or pause and persist beyond app termination. For mobile network scenarios, setting the CostPolicy property will indicate whether or not your app will begin or continue downloads while a metered network is being used for Internet connectivity.
The following examples will walk you through the creation and initialization of a basic download, and how to enumerate and reintroduce operations persisted from a previous app session.
Configure and start a Background Transfer file download
The following example demonstrates how strings representing a URI and a file name can be used to create a Uri object and the StorageFile that will contain the requested file. In this example, the new file is automatically placed in a pre-defined location. Alternatively, FileSavePicker can be used allow users to indicate where to save the file on the device. Note that the load method called to re-assign callbacks to the DownloadOperation, should it persist through app termination, is in the DownloadOp class defined later in this section.
function DownloadOp() { var download = null; var promise = null; var imageStream = null; this.start = function (uriString, fileName) { try { // Asynchronously create the file in the pictures folder. Windows.Storage.KnownFolders.picturesLibrary.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.generateUniqueName).done(function (newFile) { var uri = Windows.Foundation.Uri(uriString); var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader(); // Create a new download operation. download = downloader.createDownload(uri, newFile); // Start the download and persist the promise to be able to cancel the download. promise = download.startAsync().then(complete, error, progress); }, error); } catch (err) { displayException(err); } }; // On application activation, reassign callbacks for a download // operation persisted from previous application state. this.load = function (loadedDownload) { try { download = loadedDownload; printLog("Found download: " + download.guid + " from previous application run.<br\>"); promise = download.attachAsync().then(complete, error, progress); } catch (err) { displayException(err); } }; }
Note the asynchronous method calls defined using JavaScript promises. Looking at line 17 from the previous code example:
promise = download.startAsync().then(complete, error, progress);
The async method call is followed by a then statement which indicates methods, defined by the app, that are called when a result from the async method call is returned. For more information on this programming pattern, see Asynchronous programming in JavaScript using promises.
Adding additional operation control methods
The level of control can be increased by implementing additional DownloadOperation methods. For example, adding the following code to the example above will introduce the ability to cancel the download.
// Cancel download. this.cancel = function () { try { if (promise) { promise.cancel(); promise = null; printLog("Canceling download: " + download.guid + "<br\>"); if (imageStream) { imageStream.close(); } } else { printLog("Download " + download.guid + " already canceled.<br\>"); } } catch (err) { displayException(err); } };
On completion or cancellation of a DownloadOperation, any associated system resources are released. However, if your app is terminated before either of these events occur, downloads will pause and persist in the background. The following examples demonstrate how to re-introduce persisted downloads into a new app session.
Enumerate persisted operations at start-up
Before defining the function that enumerates persisted operations, we need to create an array that will contain the DownloadOperation objects that it will return:
var downloadOps = [];
Next we define the function that enumerates persisted operations and stores them in our array. Note that the load method called to re-assign callbacks for a persisted DownloadOperation is in the DownloadOp example we define later in this section.
// Enumerate outstanding downloads. Windows.Networking.BackgroundTransfer.BackgroundDownloader.getCurrentDownloadsAsync().done(function (downloads) { for (var i = 0; i < downloads.size; i++) { var download = new DownloadOp(); download.load(downloads[i]); downloadOps.push(download); } });
Note
For Windows Phone Store apps, background transfers continue to progress while your app is not in the foreground. Because your app is not running in this scenario, it will not receive a notification when the transfer completes. When your app resumes, if you just check the progress of a completed transfer, the status will be BackgroundTransferStatus.Running. However, when you attach to the transfer as in the example code above, the task completion handler will be raised and the transfer status will be updated.
Request Timeouts
There are two primary connection timeout scenarios to take into consideration:
When establishing a new connection for a transfer, the connection request is aborted if it is not established within five minutes.
After a connection has been established, an HTTP request message that has not received a response within two minutes is aborted.
In either scenario, assuming there is Internet connectivity, Background Transfer will automatically retry a request up to three times. In the event Internet connectivity is not detected, additional requests will wait until it is.
For XHR operations, specific timeout values can be defined using the WinJS.Promise.timeout property. For more information on how this is accomplished, see Setting time-out values when using WinJS.xhr.
Debugging Guidance
Stopping a debugging session in Microsoft Visual Studio is comparable to closing your app. Even while debugging, your app should enumerate and then, resume, restart, or cancel any downloads persisted from the previous session. For example, you can have your app cancel enumerated persisted download operations at app startup if there is no interest in previous operations for the current debug session.
If there are Visual Studio project updates, like changes to the app manifest, and the app is uninstalled and re-deployed, GetCurrentUploadsAsync cannot enumerate operations created using the previous app deployment.
See Debugging and testing Windows Store apps for more information.
When using Background Transfer during development, you may get into a situation where the internal caches of active and completed transfer operations can get out of sync. This may result in the inability to start new transfer operations or interact with existing operations and BackgroundTransferGroup objects. In some cases, attempting to interact with existing operations may trigger a crash. This result can occur if the TransferBehavior property is set to Parallel. This issue occurs only in certain scenarios during development and is not applicable to end users of your app.
Four scenarios using Visual Studio can cause this issue.
- You create a new project with the same app name as an existing project, but a different language (from C++ to C#, for example).
- You change the target architecture (from x86 to x64, for example) in an existing project.
- You change the culture (from neutral to en-US, for example) in an existing project.
- You add or remove a capability in the package manifest (adding Enterprise Authentication, for example) in an existing project.
Regular app servicing, including manifest updates which add or remove capabilities, do not trigger this issue on end user deployments of your app.
To work around this issue, completely uninstall all versions of the app and re-deploy with the new language, architecture, culture, or capability. This can be done via the Start screen or using PowerShell and the Remove-AppxPackage cmdlet.
Summary and next steps
In this topic we reviewed how to download files using the and Background Transfer APIs in JavaScript. We reviewed the differences between the two and highlighted how practical application depends on the size and lifetime of a file download.
You can also use XHR and Background Transfer to upload files. For an explanation of core concepts and examples, see How to upload a file.
Related topics
Other
Asynchronous programming in JavaScript using promises
Create your first Windows Runtime app using JavaScript
How to configure network capabilities
How to download a file with WinJS xhr
Reference
Windows.Networking.BackgroundTransfer
Samples