You can use WinJS.Promise.join to handle multiple promises.
WinJS.Promise.join allows you to handle the promises one at a time, if necessary, and also to do further handling after all the promises are completed.
In this topic, you'll learn to do a background transfer of multiple files. When you want your app to perform multiple data transfers, you need to decide what happens to the upload or download when your app is suspended.
Important Windows Store apps are full-screen apps. Apps that are not currently in use are said to be suspended, that is, moved to the background but not terminated. For more information about the app lifecycle, see Launching, resuming, and multitasking.
If you want the file transfer to stop when the app is suspended, you should use a method that doesn't create a background process, such as WinJS.xhr. But if you want the file transfer to continue even though the app is suspended, you must use the Background Transfer API. To find out more about background transfers, see Transferring data in the background.
To set up your app for network upload and access to the Pictures Library
In this procedure, you'll learn how to do a background upload of files from the user's pictures library to an upload server.
In Windows Store apps , you need to add certain capabilities, such as accessing a particular library on your hard drive or accessing the network, if you want to perform them. For more info about capabilities, see App capability declarations.
-
Create a blank Windows JavaScript app.
-
Open the package.appxmanifest file and go to the Capabilities tab.
-
The Internet (Client) capability should already be selected, but if it isn't, select it.
-
Select the Pictures Library capability.
To implement the upload process
To demonstrate how to do a background transfer, we'll set up a simple app that starts the upload when you click the Start button and stops it when you click the Stop button.
-
In the app you just created, add Start and Stop buttons.
<button id="start">Start</button> <button id="stop">Stop</button>
-
Add a DIV element to report on the files uploaded, and give it an ID of "picDiv".
<div id="picDiv"></div>
-
Add event handlers for the Start and Stop buttons to start and cancel the upload process. You can use WinJS.Utilities.ready to add the handlers.
WinJS.Utilities.ready(function () { document.getElementById("start").addEventListener("click", startUpload, false); document.getElementById("stop").addEventListener("click", stopUpload, false); });
-
Create the event handlers.
function startUpload() { } function stopUpload() { }
-
To implement the upload process, you first get the set of files in the user's Pictures Library by using getFilesAsync. You then create an upload process for each of the files by using createUpload and then start each process asynchronously by using startAsync. The getFilesAsync method returns a set of files asynchronously, so you need to handle multiple promises.
To handle multiple promises, you use WinJS.Promise.join, which takes a set of promises and itself returns a promise. You use Array.map to return the array of promises used in WinJS.Promise.join. In the Array.map callback function, you return the promise that results from creating and starting the file upload.
In this example, you notify the app when an upload has been completed by writing to the DIV, and you report errors in the same place.
// This variable is used to cancel the upload operation. var uploading = null; function startUpload() { var picDiv = document.getElementById("picDiv"); var picturesLibrary = Windows.Storage.KnownFolders.picturesLibrary; var uploader = Windows.Networking.BackgroundTransfer.BackgroundUploader(); // The uploading variable is used in the cancel operation. uploading = picturesLibrary.getFilesAsync().then(function (files) { return WinJS.Promise.join( // Create an array of promises that carry out the upload processes. files.map(function (file) { // Create the uploads. return uploader.createUpload(new Windows.Foundation.Uri(<upload location> + file.name), file). // Start the uploads. startAsync().then( // Report when each upload has completed. function (result) { picDiv.textContent += result.sourceFile.name + " | "; }, // Report when the upload results in an error. (Don't report on cancellations here.) function (error) { if (error.name !== "Canceled") { picDiv.textContent = "Error: " + error.message; } } ); }) ).then( // Report when all uploads are complete. function (result) { picDiv.textContent += " | uploaded files | "; }, // Report when the upload process results in an error or is canceled. function (error) { if (error.name === "Canceled") { picDiv.textContent += " canceled | "; } else { picDiv.textContent = "Error: " = error.message; } } ); }); }
- Try running the app now. You should see the Start and Stop buttons. If you click the Start button, the names of the files should appear below the buttons as the files are uploaded. The Stop button, of course, still doesn't do anything. You'll see how to cancel the upload process in the next section.
To cancel the upload process
You can cancel the upload process by simply calling cancel on the upload process you created in the previous section.
-
Use the upload variable that you created in the previous code example to call cancel.
function stopUpload(ev) { if (uploading) uploading.cancel(); }
-
Try running the app again. You should be able to start the upload process by using the Start button and stop it by using the Stop button.
To show the results of background transfers after the app is suspended and resumed
At this point the way the app reports uploads is not quite accurate, because the background upload processes continue even after the app is suspended. You need to decide how to report uploads. You can do one of the following:
-
Don't report uploads that are performed when the app is suspended at all. This is what happens with the current code.
-
Report only uploads that are performed when the app is suspended, and not those that are performed when the app is stopped. You do this by handling the resuming event.
In this example we assume that at startup users don't want to see reports about previous uploads, but only about uploads that were performed while the app was suspended.
-
To register an event handler for the resuming event, add this code:
Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function (ev) { }, false);
You need to use this form of addEventListener because the Windows Library for JavaScript Application object doesn't register for the resuming event.
-
In the event handler, call getCurrentUploadsAsync and report the results to the DIV element, as before.
Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function (ev) { // App is reactivated from suspension, so restore state. Windows.Networking.BackgroundTransfer.BackgroundUploader.getCurrentUploadsAsync().then( function (uploads) { // If there are uploads from previous app state, finish them. return WinJS.Promise.join( uploads.map(function (upload) { upload.attachAsync().then( function (result) { picDiv.textContent += result.sourceFile.name + " | "; }, function (error) { picDiv.textContent = "error"; }); })); }); WinJS.UI.processAll(); }, false);
-
Start debugging the app. To test the resuming event handler, you need to start the upload process and then suspend and resume the app. You can do this while you are debugging by clicking the Suspend button on the Visual Studio toolbar. To resume the app, click the arrow on the right side of the Suspend button. You will see these choices: Suspend, Resume, Suspend and shutdown. Select Resume. (If you want to suspend the app again, you need to click the arrow on the right of the button, which is now labeled Resume, and select Suspend.) For more information about suspending and resuming, see Trigger suspend, resume, and background events.
Full example
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MultipleUploads</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
<!-- MultipleUploads references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
</head>
<body>
<button id="start">Start</button>
<button id="stop">Stop</button>
<div id="picDiv"></div>
<script type="text/javascript">
Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function (ev) {
// App is reactivated from suspension, so restore state.
Windows.Networking.BackgroundTransfer.BackgroundUploader.getCurrentUploadsAsync().then(
function (uploads) {
// If there are uploads from previous app state, finish them.
return WinJS.Promise.join(
uploads.map(function (upload) {
upload.attachAsync().then(
function (result) {
picDiv.textContent += result.sourceFile.name + " | ";
},
function (error) {
picDiv.textContent = "error";
});
}));
});
WinJS.UI.processAll();
}, false);
// This variable is used to cancel the upload operation.
var uploading = null;
WinJS.Utilities.ready(function () {
document.getElementById("start").addEventListener("click", startUpload);
document.getElementById("stop").addEventListener("click", stopUpload);
}, false);
function startUpload() {
var picDiv = document.getElementById("picDiv");
var picturesLibrary = Windows.Storage.KnownFolders.picturesLibrary;
var uploader = Windows.Networking.BackgroundTransfer.BackgroundUploader();
// The uploading variable is used in the cancel operation.
uploading = picturesLibrary.getFilesAsync().then(function (files) {
return WinJS.Promise.join(
// Create an array of promises that carry out the upload processes.
files.map(function (file) {
// Create the uploads.
return uploader.createUpload(new Windows.Foundation.Uri(<upload location>), file).
// Start the uploads.
startAsync().then(
// Report when each upload is completed.
function (result) {
picDiv.textContent += result.sourceFile.name + " | ";
},
// Report when the upload results in an error. (Don't report cancellations here.)
function (error) {
if (error.name !== "Canceled") {
picDiv.textContent = "Error: " + error.message;
}
}
);
})
).then(
// Report when all uploads are complete.
function (result) {
picDiv.textContent += " | uploaded files | ";
},
// Report when the upload process results in an error or is canceled.
function (error) {
if (error.name === "Canceled") {
picDiv.textContent += " canceled | ";
} else {
picDiv.textContent = "Error: " = error.message;
}
}
);
});
}
function stopUpload(ev) {
if (uploading)
uploading.cancel();
}
</script>
</body>
</html>
Build date: 11/29/2012