如何下载文件 (HTML)
[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]
本主题向你介绍如何下载文件。
应用可以使用本主题中讨论的 API 支持其应用与 Web 服务交互,以使用或共享流行的媒体格式,如照片、音乐和视频。
使用 JavaScript 开发应用时,可以通过两个主要选项从 Internet 上的位置请求文件。像频繁检索的网站资产这样的小文件,可以使用 XHR 下载,以便执行异步 HTTP GET 请求。该功能将一个 XMLHttpRequest 调用封装在一个 promise 中,它是在 JavaScript 中支持异步行为的编程模式。
或者,要在下载操作生命周期可能跨越多个应用挂起和/或网络功能更改的大型媒体(视频和音乐)时提供一致的体验,你的应用可以使用 Background Transfer。有关对后台传输的概要介绍,请参阅后台传输数据。
先决条件
有关创建 JavaScript 应用的常规帮助,请参阅创建你的第一个使用 JavaScript 的 Windows 运行时应用。此外,在本主题中使用 JavaScript Promise 来完成异步操作。有关此编程模式的详细信息,请参阅在 JavaScript 中使用 Promise 进行异步编程。
为了使你的应用能够使用网络,你必须在项目 Package.appxmanifest 文件中设置此功能。 有关每个网络功能的定义,请参阅如何配置网络隔离功能。
本主题中的所有后台传输示例均基于 后台传输示例。
使用 XHR 下载文件
若要使用 JavaScript 启动基本的异步 HTTP 请求,请调用 XHR 并使用 Option 参数为它提供相关请求数据。默认情况下,该方法调用是一个 GET 请求,因此仅通过 Option 提供的所需值为 URL 和 responseType。但是,很多 Web 服务都需要身份验证,并且当调用 XHR 来从任何安全的 Web 服务请求资源时应该包含这些凭据。
XHR GET 操作还要求在 Option 参数中使用 responseType 指定响应中应该有的内容类型。在我们的示例中,我们请求了一个 .png 文件,因此我们的 responseType 值为“blob”。有关支持的内容类型的完整列表以及如何请求它们的示例,请参阅如何使用 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;
});
在 JavaScript 中,每个 Promise 都有两个函数,可以用来处理异步操作、then 和 done 的结果。这两个函数都带有三个参数,下载完成时会调用一个函数(即,readyState 为 4),发生错误时会调用一个函数,正在进行下载时会调用一个函数(即,readyState 为 2 或 3)。只有 done 函数在未处理错误时引发异常。如果未提供错误函数,则首选使用此函数。
使用后台传输下载文件
如果使用后台传输,每个下载作为 DownloadOperation 存在,该对象具有一系列用于暂停、恢复、重启和取消操作的控制方法。系统根据 DownloadOperation 自动处理应用事件(如暂停或终止)和连接更改;在应用挂起期间或暂停时,下载会继续运行,并且在应用终止后,仍然保持运行。对于移动网络情形,设置 CostPolicy 属性可指示在对 Internet 连接使用按流量计费的网络时,应用是否将开始或继续下载。
以下示例将指导你完成基本下载的创建和初始化,以及如何枚举和重新引入以前应用会话中保持的操作。
配置和启动后台传输文件下载
以下示例演示如何使用表示 URI 和文件名的字符串来创建 Uri 对象和包含所请求文件的 StorageFile。在本示例中,新文件会自动放置到预定义的位置。 此外,可使用 FileSavePicker 来允许用户指示要在设备上保存文件的位置。请注意,load 方法用于重新分配 DownloadOperation 的回调,对于该方法在应用终止过程中是否应持续,将在本节后续部分的 DownloadOp 类中进行定义。
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); } }; }
请注意使用 JavaScript Promise 定义的异步方法调用。查看上一代码示例中的第 17 行:
promise = download.startAsync().then(complete, error, progress);
异步方法调用后跟一个 then 语句,它指示从异步方法调用返回结果时调用的方法(由应用定义)。有关此编程模式的详细信息,请参阅在 JavaScript 中使用 Promise 进行异步编程。
添加其他操作控制方法
控制级别可通过实现附加的 DownloadOperation 方法来进行提升。例如,将以下代码添加到上述示例将引入取消下载的功能。
// 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); } };
在完成或取消 DownloadOperation 时,将释放任何关联的系统资源。不过,如果你的应用在任一事件发生之前停止,下载将暂停并在后台保持。下例演示了如何将保持的下载重新引入新的应用会话。
在启动时枚举保持的操作
在定义枚举持续操作的函数之前,我们需要创建包含将返回的 DownloadOperation 对象的数组:
var downloadOps = [];
接下来,我们要定义函数来枚举持续操作,并将其存储到数组中。 请注意,调用的 load 方法用于重新分配持续的 DownloadOperation 的回调,该方法位于 DownloadOp 示例中,我们将在本节的后续部分进行定义。
// 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); } });
注意
对于 Windows Phone 应用商店应用,当你的应用未在前台中时,后台传输仍会继续进行。因为你的应用未在此应用场景中运行,所以当传输完成时,它将不会收到通知。当你的应用恢复时,如果你只是检查已完成传输的进度,该状态将为 BackgroundTransferStatus.Running。但是,当你连接到传输(如上面的示例代码所示)时,将引发任务完成处理程序,并且将更新传输状态。
请求超时
需要考虑两个主要的连接超时方案:
为传输建立新连接时,如果未在 5 分钟内建立连接请求,则会中止连接请求。
建立连接之后,将中止在两分钟内未收到响应的 HTTP 请求消息。
在任何一种方案中,假定存在 Internet 连接,后台传输将最多自动重试一个请求三次。如果未检测到 Internet 连接,则其他请求将一直等待,直到检测到 Internet 连接。
对于 XHR 操作,可以使用 WinJS.Promise.timeout 属性定义特定的超时值。有关如何实现该操作的详细信息,请参阅使用 WinJS.xhr 时设置超时值。
调试指南
在 Microsoft Visual Studio 中停止调试会话与关闭你的应用相似。 即使在调试时,你的应用也应该枚举,然后恢复、重新启动或取消任何在上一个会话中保持的下载。例如,如果对当前调试会话之前的操作没有兴趣,你可以使应用在应用启动时取消已枚举的持续下载操作。
如果存在 Visual Studio 项目更新(例如,对应用清单的更改),并且应用被卸载并重新部署,GetCurrentUploadsAsync 不能枚举使用之前的应用部署创建的操作。
有关详细信息,请参阅调试和测试 Windows 应用商店应用。
在部署期间使用后台传输时,你可能会遇到一种情况,即可用的和已完成的传输操作的内部缓存不同步。这可能会导致不能启动新的传输操作,或者不能与现有操作和 BackgroundTransferGroup 对象交互。在某些情况下,尝试与现有操作交互可能引发崩溃。如果 TransferBehavior 属性设置为 Parallel,则可能发生该结果。该问题仅在开发期间的某些应用场景中发生,该情况不适用于应用的最终用户。
使用 Visual Studio 的四种应用场景可能会导致该问题。
- 你用于创建新项目的应用名称与现有项目相同,但语言不同(例如从 C++ 改为 C#)。
- 你更改了现有项目中的目标体系结构(例如,从 x86 改为 x64)。
- 你更改了现有项目中的区域性(例如,从中性改为 en-US)。
- 你在现有项目的程序包清单中添加或删除了一项功能(例如,添加“企业身份验证”)。
常规应用服务(包含可添加或删除功能的清单更新)不会在应用的最终用户部署时导致该问题。
要解决该问题,请彻底卸载应用的所有版本,并采用新的语言、体系结构、区域性或功能重新部署。可通过“开始”****屏幕或使用 PowerShell 和 Remove-AppxPackage cmdlet 完成此操作。
摘要和后续步骤
在本主题中,我们介绍了如何使用 JavaScript 中的 Background Transfer API 来下载文件。我们介绍了这两种方法之间的区别并且重点介绍了实际的应用程序如何依赖文件下载的大小和生命周期。
你还可以使用 XHR 和后台传输上载文件。有关核心概念和示例说明,请参阅如何上载文件。
相关主题
其他
使用 JavaScript 中的 Promises 进行异步编程
创建你的第一个使用 JavaScript 的 Windows 运行时应用
参考
Windows.Networking.BackgroundTransfer
示例