编写你的应用程序(九)、网址加载

原文链接:https://developer.chrome.com/native-client/devguide/coding/url-loading 

注意:已针对ChromeOS以外的平台公布了此处所述技术的弃用。
请访问我们的 迁移指南 了解详情。


网址加载

介绍

本节介绍如何使用URLLoader API将服务器中的图像和声音文件等资源加载到应用程序中。

本节中讨论的示例包含在目录中的SDK中 examples/api/url_loader

参考信息

有关从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文件,其内容使用URLLoaderAPI 检索。
  • url_loader.cc - 设置和提供和进入Native客户端模块的入口点的代码。
  • url_loader_handler.cc - 检索url_loader_success.html文件内容并返回结果的代码(这是完成大部分工作的地方)。

本文档的其余部分介绍了url_loader.cc和 url_loader_handler.cc文件中的代码。

网址加载概述

与许多Pepper API一样,URLLoaderAPI包含一组异步执行的方法,并在Native Client模块中调用回调函数。该url_loader示例的高级流程如下所述。请注意,命名空间pp::URLLoader中的方法是Pepper URLLoaderAPI的一部分 ,而其余​​的函数是Native Client模块中的代码的一部分(特别是在文件中url_loader_handler.cc)。下图显示了url_loader_handler代码的流程:

/native-client/images/pepper-urlloader-api.png

以下是URL加载中涉及的高级步骤。

  1. Native Client模块调用pp::URLLoader::Open以开始打开URL。
  2. Open完成时,它调用本机客户端模块中的回调函数(在这种情况下,OnOpen)。
  3. Native Client模块调用Pepper函数 URLLoader::ReadResponseBody以开始读取包含数据的响应主体。ReadResponseBody在Native Client模块中传递一个可选的回调函数(在本例中为On Read)。回调函数是一个可选的回调函数,因为ReadResponseBody如果数据可用,它可以读取数据并同步返回(这可以提高大文件和快速连接的性能)。

本文档的其余部分演示了如何在url_loader示例中实现前面的步骤。

url_loader 深入解读

设置请求

HandleMessagein 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();
      }
    }
  }
}

请注意,URLLoaderHandlerin 的构造函数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);
}

下载数据

Startin 使用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许可下提供的内容

原文地址:https://www.cnblogs.com/SunkingYang/p/11049125.html