原文链接:https://developer.chrome.com/native-client/devguide/coding/url-loading
注意:已针对ChromeOS以外的平台公布了此处所述技术的弃用。
请访问我们的 迁移指南 了解详情。
网址加载
介绍
本节介绍如何使用URLLoader API将服务器中的图像和声音文件等资源加载到应用程序中。
本节中讨论的示例包含在目录中的SDK中 examples/api/url_loader
。
参考信息
有关从URL加载数据的参考信息,请参阅以下文档:
- url_loader.h - 包含
URLLoader
用于从URL加载数据的类 - url_request_info.h - 包含
URLRequest
用于创建和操作URL请求的类 - url_response_info.h - 包含
URLResponse
用于examaning URL响应的类
背景
当用户启动Native Client Web应用程序时,Chrome会下载并缓存应用程序的HTML文件,清单文件(.nmf)和Native Client模块(.pexe或.nexe)。如果您的应用程序需要其他资源(如图像和声音文件),则必须明确加载这些资产。您可以使用本节中介绍的Pepper API将URL中的资源加载到应用程序中。
将资产加载到应用程序后,Chrome将缓存这些资产。但是,为了避免出现Chrome缓存的奇思妙想,您可能希望使用Pepper FileIO API将这些资源写入用户文件系统上的持久沙盒位置。
这个url_loader
例子
SDK包含一个示例,用于url_loader
演示从服务器下载文件。此示例包含以下主要文件:
index.html
- 启动Native Client模块的HTML代码。example.js
- index.html的JavaScript文件。它具有在单击“获取URL”按钮时向Native Client模块发送PostMessage请求的代码。url_loader_success.html
- 服务器上的HTML文件,其内容使用URLLoader
API 检索。url_loader.cc
- 设置和提供和进入Native客户端模块的入口点的代码。url_loader_handler.cc
- 检索url_loader_success.html文件内容并返回结果的代码(这是完成大部分工作的地方)。
本文档的其余部分介绍了url_loader.cc
和 url_loader_handler.cc
文件中的代码。
网址加载概述
与许多Pepper API一样,URLLoader
API包含一组异步执行的方法,并在Native Client模块中调用回调函数。该url_loader
示例的高级流程如下所述。请注意,命名空间pp::URLLoader
中的方法是Pepper URLLoader
API的一部分 ,而其余的函数是Native Client模块中的代码的一部分(特别是在文件中url_loader_handler.cc
)。下图显示了url_loader_handler
代码的流程:
以下是URL加载中涉及的高级步骤。
- Native Client模块调用
pp::URLLoader::Open
以开始打开URL。 - 当
Open
完成时,它调用本机客户端模块中的回调函数(在这种情况下,OnOpen
)。 - Native Client模块调用Pepper函数
URLLoader::ReadResponseBody
以开始读取包含数据的响应主体。ReadResponseBody
在Native Client模块中传递一个可选的回调函数(在本例中为OnRead
)。回调函数是一个可选的回调函数,因为ReadResponseBody
如果数据可用,它可以读取数据并同步返回(这可以提高大文件和快速连接的性能)。
本文档的其余部分演示了如何在url_loader
示例中实现前面的步骤。
url_loader
深入解读
设置请求
HandleMessage
in url_loader.cc
创建一个URLLoaderHandler
实例并将其传递给要检索的资产的URL。然后HandleMessage
调用Start
开始从服务器检索资产:
void URLLoaderInstance::HandleMessage(const pp::Var& var_message) {
if (!var_message.is_string()) {
return;
}
std::string message = var_message.AsString();
if (message.find(kLoadUrlMethodId) == 0) {
// The argument to getUrl is everything after the first ':'.
size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
if (sep_pos != std::string::npos) {
std::string url = message.substr(sep_pos + 1);
printf("URLLoaderInstance::HandleMessage('%s', '%s')
",
message.c_str(),
url.c_str());
fflush(stdout);
URLLoaderHandler* handler = URLLoaderHandler::Create(this, url);
if (handler != NULL) {
// Starts asynchronous download. When download is finished or when an
// error occurs, |handler| posts the results back to the browser
// vis PostMessage and self-destroys.
handler->Start();
}
}
}
}
请注意,URLLoaderHandler
in 的构造函数url_loader_handler.cc
设置了URL请求的参数(使用 SetURL,
SetMethod
和,SetRecordDownloadProgress
):
URLLoaderHandler::URLLoaderHandler(pp::Instance* instance,
const std::string& url)
: instance_(instance),
url_(url),
url_request_(instance),
url_loader_(instance),
buffer_(new char[READ_BUFFER_SIZE]),
cc_factory_(this) {
url_request_.SetURL(url);
url_request_.SetMethod("GET");
url_request_.SetRecordDownloadProgress(true);
}
下载数据
Start
in 使用a url_loader_handler.cc
创建一个callback(cc
) CompletionCallbackFactory
。传递回调以Open
在完成时调用。Open
开始加载URLRequestInfo
。
void URLLoaderHandler::Start() {
pp::CompletionCallback cc =
cc_factory_.NewCallback(&URLLoaderHandler::OnOpen);
url_loader_.Open(url_request_, cc);
}
OnOpen
确保Open调用成功,如果是,则调用 GetDownloadProgress
以确定要下载的数据量,以便为响应正文分配内存。
请注意,要下载的数据量可能是未知的,在这种情况下 GetDownloadProgress
设置total_bytes_to_be_received
为-1。如果total_bytes_to_be_received
设置为-1或GetDownloadProgress
失败则不是问题; 在这些情况下,读缓冲区的存储器不能提前分配,必须在接收数据时分配。
最后,OnOpen回调
ReadBody.
void URLLoaderHandler::OnOpen(int32_t result) {
if (result != PP_OK) {
ReportResultAndDie(url_, "pp::URLLoader::Open() failed", false);
return;
}
int64_t bytes_received = 0;
int64_t total_bytes_to_be_received = 0;
if (url_loader_.GetDownloadProgress(&bytes_received,
&total_bytes_to_be_received)) {
if (total_bytes_to_be_received > 0) {
url_response_body_.reserve(total_bytes_to_be_received);
}
}
url_request_.SetRecordDownloadProgress(false);
ReadBody();
}
ReadBody
创建另一个CompletionCallback
(a NewOptionalCallback
)并将其传递给ReadResponseBody,
读取响应主体, AppendDataBytes,
并将结果数据附加到先前读取的数据。
void URLLoaderHandler::ReadBody() {
pp::CompletionCallback cc =
cc_factory_.NewOptionalCallback(&URLLoaderHandler::OnRead);
int32_t result = PP_OK;
do {
result = url_loader_.ReadResponseBody(buffer_, READ_BUFFER_SIZE, cc);
if (result > 0) {
AppendDataBytes(buffer_, result);
}
} while (result > 0);
if (result != PP_OK_COMPLETIONPENDING) {
cc.Run(result);
}
}
void URLLoaderHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) {
if (num_bytes <= 0)
return;
num_bytes = std::min(READ_BUFFER_SIZE, num_bytes);
url_response_body_.insert(
url_response_body_.end(), buffer, buffer + num_bytes);
}
最终要么已经为整个文件读取了所有字节(结果为PP_OK
或0),所有字节都已经被读取了已经下载的内容,但更多的是要下载(PP_OK_COMPLETIONPENDING
或-1),或者有错误(少于比-1)。OnRead
如果发生错误或被调用PP_OK
。
显示结果
ReportResultAndDie
当出现错误或PP_OK
返回时,OnRead调用以指示文件流已完成。ReportResultAndDie
然后调用ReportResult,
哪些调用PostMessage
将结果发送回HTML页面。
CC-By 3.0许可下提供的内容