如何上载文件 (HTML)
[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]
本主题向你介绍如何将来自设备的数据或文件上载到 Internet。
应用可以使用本主题中讨论的 API 支持其应用与 Web 服务交互,以使用或共享流行的媒体格式,如照片、音乐和视频。
对于操作生命周期可能跨越多个应用挂起和/或网络功能更改的大型基于流的传输或分段传输(视频、音乐和大型图像),你的应用可以使用 Background Transfer。
有关对后台传输的概要介绍,请参阅后台传送数据。
先决条件
有关创建 JavaScript Windows 应用商店应用的常规帮助,请参阅创建你的第一个使用 JavaScript 的 Windows 运行时应用。此外,在本主题中使用 JavaScript Promise 来完成异步操作。有关此编程模式的详细信息,请参阅在 JavaScript 中使用 Promise 进行异步编程。
为了使你的应用能够使用网络,你必须在项目 Package.appxmanifest 文件中设置此功能。 有关每个网络功能的定义,请参阅如何配置网络隔离功能。
以下后台传输示例使用 JavaScript,且基于 后台传输示例。
后台传输上载操作
如果使用后台传输,上载作为 UploadOperation 存在,该对象具有一系列用于重启和取消操作的控制方法。 系统根据 UploadOperation 自动处理应用事件(如暂停或终止)和连接更改;在应用挂起期间或暂停时,上载会继续运行,并且在应用终止后,仍然保持运行。此外,正确设置 CostPolicy 可指示在对 Internet 连接使用按流量计费的网络时,应用是否将开始上载。
以下示例将指导你完成基本上载的创建和初始化,以及如何枚举和重新引入以前应用会话中保持的操作。
上载文件
创建上载从 BackgroundUploader 开始。该类用于提供使应用能够在创建结果 UploadOperation 之前配置上载的方法。以下示例说明如何使用所需的 Uri 和 StorageFile 对象执行该操作。
标识文件和目标以用于上载
我们首先需要识别要上载的目标位置的 URI 和要上载的文件,然后才能开始创建 UploadOperation。在以下示例中,uriString 值使用 UI 输入的字符串进行填充,file 值使用 PickSingleFileAsync 操作返回的 StorageFile 对象进行填充。
function uploadFile() { var filePicker = new Windows.Storage.Pickers.FileOpenPicker(); filePicker.fileTypeFilter.replaceAll(["*"]); filePicker.pickSingleFileAsync().then(function (file) { if (!file) { printLog("No file selected"); return; } var upload = new UploadOp(); var uriString = document.getElementById("serverAddressField").value; upload.start(uriString, file); // Store the upload operation in the uploadOps array. uploadOperations.push(upload); }); }
创建和初始化上载操作
在前一步骤中,uriString 和 file 值会传递给下一个示例 UploadOp 的实例,在该示例中,这些值用于配置和启动新的上载操作。首先,uriString 经过解析来创建所需的 Uri 对象。
其次,BackgroundUploader 使用提供的 StorageFile (file) 的属性来填充请求头并使用 StorageFile 对象设置 SourceFile 属性。然后,调用 SetRequestHeader 方法以插入文件名(以字符串形式提供)和 StorageFile.Name 属性。
最后,BackgroundUploader 会创建 UploadOperation (upload)。
function UploadOp() { var upload = null; var promise = null; this.start = function (uriString, file) { try { var uri = new Windows.Foundation.Uri(uriString); var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); // Set a header, so the server can save the file (this is specific to the sample server). uploader.setRequestHeader("Filename", file.name); // Create a new upload operation. upload = uploader.createUpload(uri, file); // Start the upload and persist the promise to be able to cancel the upload. promise = upload.startAsync().then(complete, error, progress); } catch (err) { displayError(err); } }; // On application activation, reassign callbacks for a upload // operation persisted from previous application state. this.load = function (loadedUpload) { try { upload = loadedUpload; promise = upload.attachAsync().then(complete, error, progress); } catch (err) { displayError(err); } }; }
请注意使用 JavaScript Promise 定义的异步方法调用。查看上个示例中的一行:
promise = upload.startAsync().then(complete, error, progress);
异步方法调用后跟一个 then 语句,它指示从异步方法调用返回结果时调用的方法(由应用定义)。有关此编程模式的详细信息,请参阅在 JavaScript 中使用 Promise 进行异步编程。
上载多个文件
标识文件和目标以用于上载
在涉及到使用单个 UploadOperation 传输多个文件的方案中,这一过程将如通常一般开始,首先提供必要的目标 URI 和本地文件信息。与上一部分中的示例类似,由最终用户以字符串形式提供 URI,并且也可以使用 FileOpenPicker 提供通过用户界面指示文件的功能。但在此方案中,应用应改为调用 PickMultipleFilesAsync 方法,以便能够通过用户界面选择多个文件。
function uploadFiles() { var filePicker = new Windows.Storage.Pickers.FileOpenPicker(); filePicker.fileTypeFilter.replaceAll(["*"]); filePicker.pickMultipleFilesAsync().then(function (files) { if (files === 0) { printLog("No file selected"); return; } var upload = new UploadOperation(); var uriString = document.getElementById("serverAddressField").value; upload.startMultipart(uriString, files); // Persist the upload operation in the global array. uploadOperations.push(upload); }); }
创建给定参数的对象
下面的两个示例使用单示例方法 startMultipart(在上一步结束时调用)中包含的代码。为了进行说明,创建 BackgroundTransferContentPart 对象数组的方法中的代码已从创建结果 UploadOperation 的代码中分离出来。
首先,用户提供的 URI 字符串初始化为 Uri。接下来,循环访问传递到此方法的 IStorageFile 对象 (files) 数组,使用每个对象创建一个新的 BackgroundTransferContentPart 对象,然后将其置于 contentParts 数组中。
upload.startMultipart = function (uriString, files) { try { var uri = new Windows.Foundation.Uri(uriString); var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); var contentParts = []; files.forEach(function (file, index) { var part = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart("File" + index, file.name); part.setFile(file); contentParts.push(part); });
创建和初始化分段上载操作
在使用所有 BackgroundTransferContentPart 对象(表示每个要上载的 IStorageFile)填充 contentParts 数组后,我们将准备使用 Uri 调用 CreateUploadAsync 以指示将请求发送到哪里。
// Create a new upload operation. uploader.createUploadAsync(uri, contentParts).then(function (uploadOperation) { // Start the upload and persist the promise to be able to cancel the upload. upload = uploadOperation; promise = uploadOperation.startAsync().then(complete, error, progress); }); } catch (err) { displayError(err); } };
在启动时枚举保持的操作
在完成或取消 UploadOperation 时,将释放关联的任何系统资源。然而,如果在发生这些操作之前终止应用,将中止任何活动操作,并仍将占有与每个操作相关的资源。如果未枚举这些操作并且未重新引入下一个应用会话,那么它们将不会完成并将继续占有设备资源。
在定义枚举持续操作的函数之前,我们需要创建包含将返回的 UploadOperation 对象的数组:
var uploadOperations = [];
接下来,我们要定义函数来枚举持续操作,并将其存储到数组中。 请注意,load 方法用于重新分配 UploadOperation 的回调,对于该方法在应用终止过程中是否应持续,我们将在本节后续部分的 UploadOp 类中进行定义。
function Windows.Networking.BackgroundTransfer.BackgroundUploader.getCurrentUploadsAsync() { .then(function (uploads) { for (var i = 0; i < uploads.size; i++) { var upload = new UploadOp(); upload.load(uploads[i]); uploadOperations.push(upload); } } };
注意
对于 Windows Phone 应用商店应用,当你的应用未在前台中时,后台传输仍会继续进行。因为你的应用未在此应用场景中运行,所以当传输完成时,它将不会收到通知。当你的应用恢复时,如果你只是检查已完成传输的进度,该状态将为 BackgroundTransferStatus.Running。但是,当你连接到传输(如上面的示例代码所示)时,将引发任务完成处理程序,并且将更新传输状态。
请求超时
需要考虑两个主要的连接超时方案:
为传输建立新连接时,如果未在 5 分钟内建立连接请求,则会中止连接请求。
建立连接之后,会中止在两分钟内未收到响应的 HTTP 请求消息。
注意 在任何一种方案中,假定存在 Internet 连接,后台传输将最多自动重试一个请求三次。如果未检测到 Internet 连接,则其他请求将一直等待,直到检测到 Internet 连接。
调试指南
在 Microsoft Visual Studio 中停止调试会话与关闭你的应用相似;PUT 上载将暂停,POST 上载将终止。即使在调试时,你的应用也应该枚举,然后重新启动或取消任何保持的上载。例如,如果对该调试会话之前的操作没有兴趣,你可以使应用在应用启动时取消已枚举的持续上载操作。
在调试会话期间,当系统随着应用启动开始枚举下载/上载时,如果对该调试会话之前的操作没有兴趣,你可以让应用取消它们。请注意,如果存在 Visual Studio 项目更新(例如,对应用清单的更改),并且应用被卸载并重新部署,GetCurrentUploadsAsync 不能枚举使用之前的应用部署创建的操作。
有关详细信息,请参阅调试和测试 Windows 应用商店应用。
在部署期间使用后台传输时,你可能会遇到一种情况,即可用的和已完成的传输操作的内部缓存不同步。这可能会导致不能启动新的传输操作,或者不能与现有操作和 BackgroundTransferGroup 对象交互。在某些情况下,尝试与现有操作交互可能引发崩溃。如果 TransferBehavior 属性设置为 Parallel,则可能发生该结果。该问题仅在开发期间的某些应用场景中发生,该情况不适用于应用的最终用户。
使用 Visual Studio 的四种应用场景可能会导致该问题。
- 你用于创建新项目的应用名称与现有项目相同,但语言不同(例如从 C++ 改为 C#)。
- 你更改了现有项目中的目标体系结构(例如,从 x86 改为 x64)。
- 你更改了现有项目中的区域性(例如,从中性改为 en-US)。
- 你在现有项目的程序包清单中添加或删除了一项功能(例如,添加“企业身份验证”)。
常规应用服务(包含可添加或删除功能的清单更新)不会在应用的最终用户部署时导致该问题。
要解决该问题,请彻底卸载应用的所有版本,并采用新的语言、体系结构、区域性或功能重新部署。可通过“开始”****屏幕或使用 PowerShell 和 Remove-AppxPackage cmdlet 完成此操作。
摘要和后续步骤
在本主题中,我们介绍了如何使用 JavaScript 中的 Background Transfer API 来上载文件。
你也可以在 Windows 应用商店应用中下载文件。有关核心概念和示例说明,请参阅如何下载文件。
相关主题
其他
使用 JavaScript 中的 Promises 进行异步编程
创建你的第一个使用 JavaScript 的 Windows 运行时应用
参考
Windows.Networking.BackgroundTransfer
示例