zoukankan      html  css  js  c++  java
  • 编写你的应用程序(五)、文件I/O

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

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


    文件I / O.

    介绍

    本节介绍如何使用FileIO API使用本地安全数据存储读取和写入文件。

    您可以将File IO API与URL Loading API一起使用,为您的NaCl应用程序创建整体数据下载和缓存解决方案。例如:

    1. 使用文件IO API检查本地磁盘以查看是否存在程序所需的文件。
    2. 如果文件存在于本地,请使用File IO API将其加载到内存中。如果文件在本地不存在,请使用URL Loading API从服务器检索文件。
    3. 使用File IO API将文件写入磁盘。
    4. 在应用程序需要时使用File IO API将文件加载到内存中。

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

    参考信息

    有关FileIO的参考信息,请参阅以下文档:

    • file_io.h - 用于创建FileIO对象的API
    • file_ref.h - 用于为文件系统中的文件创建文件引用或“弱指针”的API
    • file_system.h - 用于创建与文件关联的文件系统的API

    本地文件I / O.

    Chrome在磁盘上提供了一个模糊的限制区域,Web应用程序可以安全地读取和写入文件。Pepper FileIO,FileRef和FileSystem API(统称为文件IO API)允许您访问此沙盒本地磁盘,以便您可以自己读取和写入文件以及管理缓存。数据在启动Chrome之间保持不变,除非您的应用程序删除它或用户手动删除它,否则不会删除。除了本地驱动器上的可用实际空间之外,您可以使用的本地数据量没有限制。

    启用本地文件I / O.

    启用持久性本地数据编写的最简单方法是在Chrome Web Store清单文件中包含unlimitedStorage权限。使用此权限,您可以使用Pepper FileIO API,而无需在运行时请求磁盘空间。当用户安装应用时,Chrome会显示一条消息,通知该应用已写入本地磁盘。

    如果您不使用该unlimitedStorage权限,则必须包含调用HTML5配额管理API的 JavaScript代码,以便在使用FileIO API之前显式请求本地磁盘空间。在这种情况下,Chrome会在每次制作时提示用户接受requestQuota调用。

    测试本地文件I / O.

    您应该知道,使用unlimitedStorage清单权限会限制您测试应用程序的方式。运行本机客户端应用程序中描述的四种技术中的三种 读取Chrome Web Store清单文件并unlimitedStorage 在出现时启用权限,但第一种技术(本地服务器)不会。如果要使用简单的本地服务器测试应用程序的文件IO部分,则需要包含调用HTML5配额管理API的JavaScript代码。在交付应用程序时,您可以使用unlimitedStorage清单权限替换此代码 。

    这个file_io例子

    Native Client SDK包含一个示例,file_io演示如何读取和写入本地磁盘文件。由于您可能从没有Chrome Web Store清单文件的本地服务器运行该示例,因此该示例的索引文件使用JavaScript执行上述配额管理设置。该示例包含以下主要文件:

    • index.html - 启动Native Client模块并显示用户界面的HTML代码。
    • example.js - 请求配额的JavaScript代码(如上所述)。它还监听用户与用户界面的交互,并将请求转发给Native Client模块。
    • file_io.cc - 设置并提供Native Client模块入口点的代码。

    本节的其余部分介绍了file_io.cc文件中用于读写文件的代码。

    文件I / O概述

    与许多Pepper API一样,File IO API包含一组异步执行并在Native Client模块中调用回调函数的方法。与大多数其他示例不同,该file_io示例还演示了如何在工作线程上同步进行Pepper调用。

    在模块的主线程上对Pepper进行阻塞调用是违法的。在工作线程上运行时,此限制被取消 - 这称为“从主线程调用Pepper”。这通常简化了代码的逻辑; 可以从工作线程上的一个函数调用多个异步Pepper函数,因此您可以正常使用堆栈和标准控制流结构。

    file_io示例的高级流程如下所述。请注意,命名空间pp中的方法是Pepper C ++ API的一部分。

    创建和编写文件

    以下是创建和写入文件所涉及的高级步骤:

    1. pp::FileIO::OpenPP_FILEOPEN_FLAG_CREATE标志调用来创建文件。因为回调函数是pp::BlockUntilComplete,所以此线程被阻塞直到Open成功或失败。
    2. pp::FileIO::Write被调用来写内容。同样,线程被阻塞,直到Write完成调用。如果要写入更多数据,Write则再次调用。
    3. 当没有更多数据要写时,请致电pp::FileIO::Flush

    打开并读取文件

    以下是打开和读取文件所涉及的高级步骤:

    1. pp::FileIO::Open被调用来打开文件。因为回调函数是pp::BlockUntilComplete,所以在Open成功或失败之前该线程被阻塞。
    2. pp::FileIO::Query被调用来查询有关文件的信息,例如文件大小。线程被阻塞直到Query完成。
    3. pp::FileIO::Read被叫来阅读内容。线程被阻塞直到Read完成。如果有更多数据要读取,Read则再次调用。

    删除文件

    删除文件非常简单:调用pp::FileRef::Delete。线程被阻塞直到Delete完成。

    制作目录

    制作目录也很简单:打电话pp::File::MakeDirectory。线程被阻塞直到MakeDirectory完成。

    列出目录的内容

    以下是列出目录所涉及的高级步骤:

    1. pp::FileRef::ReadDirectoryEntries被调用,并给出一个列表的目录条目。还给出了回调; 许多其他函数使用 pp::BlockUntilComplete,但ReadDirectoryEntries返回结果在其回调中,因此必须指定它。
    2. 当调用ReadDirectoryEntries完成时,它调用 ListCallback将结果打包成字符串消息,并将其发送到JavaScript。

    file_io 深潜

    file_io示例显示具有几个字段和多个按钮的用户界面。以下是该file_io示例的屏幕截图:

    /native-client/images/fileioexample.png

    每个单选按钮都是您可以执行的文件操作,文件名有一些合理的默认值。尝试在大输入框中键入消息并单击Save,然后切换到该Load File操作,然后单击Load

    让我们来看看幕后发生了什么。

    打开文件系统并准备文件I / O.

    pp::Instance::Init在创建模块的实例时调用。在这个例子中,Init启动一个新线程(通过pp::SimpleThread类),并告诉它打开文件系统:

    virtual bool Init(uint32_t /*argc*/,
                      const char * /*argn*/ [],
                      const char * /*argv*/ []) {
      file_thread_.Start();
      // Open the file system on the file_thread_. Since this is the first
      // operation we perform there, and because we do everything on the
      // file_thread_ synchronously, this ensures that the FileSystem is open
      // before any FileIO operations execute.
      file_thread_.message_loop().PostWork(
          callback_factory_.NewCallback(&FileIoInstance::OpenFileSystem));
      return true;
    }

    当文件线程开始运行时,它将调用OpenFileSystem。这将调用pp::FileSystem::Open并阻止文件线程,直到函数返回。

    需要注意的是调用pp::FileSystem::Open使用 pp::BlockUntilComplete它的回调。这是唯一可能的,因为我们正在运行主线程; 如果您尝试从主线程进行阻塞调用,该函数将返回错误 PP_ERROR_BLOCKS_MAIN_THREAD

    void OpenFileSystem(int32_t /*result*/) {
      int32_t rv = file_system_.Open(1024 * 1024, pp::BlockUntilComplete());
      if (rv == PP_OK) {
        file_system_ready_ = true;
        // Notify the user interface that we're ready
        PostMessage("READY|");
      } else {
        ShowErrorMessage("Failed to open file system", rv);
      }
    }

    处理来自JavaScript的消息

    当您单击该Save按钮时,JavaScript会将消息发布到NaCl模块,并将文件操作作为字符串发送(有关消息传递的详细信息,请参阅消息传递系统)。该字符串由解析HandleMessage,并将新工作添加到文件线程:

    virtual void HandleMessage(const pp::Var& var_message) {
      if (!var_message.is_string())
        return;
    
      // Parse message into: instruction file_name_length file_name [file_text]
      std::string message = var_message.AsString();
      std::string instruction;
      std::string file_name;
      std::stringstream reader(message);
      int file_name_length;
    
      reader >> instruction >> file_name_length;
      file_name.resize(file_name_length);
      reader.ignore(1);  // Eat the delimiter
      reader.read(&file_name[0], file_name_length);
    
      ...
    
      // Dispatch the instruction
      if (instruction == kLoadPrefix) {
        file_thread_.message_loop().PostWork(
            callback_factory_.NewCallback(&FileIoInstance::Load, file_name));
      } else if (instruction == kSavePrefix) {
        ...
      }
    }

    保存文件

    FileIoInstance::SaveSave按下按钮时调用。首先,它检查文件系统是否已成功打开:

    if (!file_system_ready_) {
      ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
      return;
    }

    然后,它pp::FileRef使用文件名创建资源。一个 FileRef资源是一个弱引用到文件系统中的文件; 也就是说,即使存在未完成的FileRef 资源,仍然可以删除文件。

    pp::FileRef ref(file_system_, file_name.c_str());

    接下来,pp::FileIO创建并打开资源。调用 pp::FileIO::Openpass PP_FILEOPEFLAG_WRITE以打开文件进行写入,PP_FILEOPENFLAG_CREATE如果尚未存在则创建新文件并PP_FILEOPENFLAG_TRUNCATE清除以前任何内容的文件:

    pp::FileIO file(this);
    
    int32_t open_result =
        file.Open(ref,
                  PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
                      PP_FILEOPENFLAG_TRUNCATE,
                  pp::BlockUntilComplete());
    if (open_result != PP_OK) {
      ShowErrorMessage("File open for write failed", open_result);
      return;
    }

    现在该文件已打开,它将以块的形式写入。在异步模型中,这需要编写一个单独的函数,将当前状态存储在免费存储和一系列回调中。因为这个函数是从主线程pp::FileIO::Write调用的,所以可以同步调用,并且可以使用传统的do / while循环:

    int64_t offset = 0;
    int32_t bytes_written = 0;
    do {
      bytes_written = file.Write(offset,
                                 file_contents.data() + offset,
                                 file_contents.length(),
                                 pp::BlockUntilComplete());
      if (bytes_written > 0) {
        offset += bytes_written;
      } else {
        ShowErrorMessage("File write failed", bytes_written);
        return;
      }
    } while (bytes_written < static_cast<int64_t>(file_contents.length()));

    最后,刷新文件以将所有更改推送到磁盘:

    int32_t flush_result = file.Flush(pp::BlockUntilComplete());
    if (flush_result != PP_OK) {
      ShowErrorMessage("File fail to flush", flush_result);
      return;
    }

    加载文件

    FileIoInstance::LoadLoad按下按钮时调用。与Save函数一样,Load首先检查FileSystem是否已成功打开,并创建一个新的FileRef

    if (!file_system_ready_) {
      ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
      return;
    }
    pp::FileRef ref(file_system_, file_name.c_str());

    接下来,Load创建并打开一个新FileIO资源,传递 PP_FILEOPENFLAG_READ打开文件进行读取。比较结果,PP_ERROR_FILENOTFOUND以便在文件不存在时提供更好的错误消息:

    int32_t open_result =
        file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete());
    if (open_result == PP_ERROR_FILENOTFOUND) {
      ShowErrorMessage("File not found", open_result);
      return;
    } else if (open_result != PP_OK) {
      ShowErrorMessage("File open for read failed", open_result);
      return;
    }

    然后Load调用pp::FileIO::Query以获取有关文件的元数据,例如其大小。这用于分配一个std::vector缓冲区,用于保存内存中文件的数据:

    int32_t query_result = file.Query(&info, pp::BlockUntilComplete());
    if (query_result != PP_OK) {
      ShowErrorMessage("File query failed", query_result);
      return;
    }
    
    ...
    
    std::vector<char> data(info.size);

    类似于Save,传统的while循环用于将文件读入新分配的缓冲区:

    int64_t offset = 0;
    int32_t bytes_read = 0;
    int32_t bytes_to_read = info.size;
    while (bytes_to_read > 0) {
      bytes_read = file.Read(offset,
                             &data[offset],
                             data.size() - offset,
                             pp::BlockUntilComplete());
      if (bytes_read > 0) {
        offset += bytes_read;
        bytes_to_read -= bytes_read;
      } else if (bytes_read < 0) {
        // If bytes_read < PP_OK then it indicates the error code.
        ShowErrorMessage("File read failed", bytes_read);
        return;
      }
    }

    最后,文件的内容被发送回JavaScript,以显示在页面上。此示例使用“ DISP|”作为显示信息的前缀命令:

    std::string string_data(data.begin(), data.end());
    PostMessage("DISP|" + string_data);
    ShowStatusMessage("Load success");

    删除文件

    FileIoInstance::DeleteDelete按下按钮时调用。首先,它检查文件系统是否已打开,并创建一个新的FileRef

    if (!file_system_ready_) {
      ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
      return;
    }
    pp::FileRef ref(file_system_, file_name.c_str());

    Save和不同LoadDeleteFileRef资源上调用,而不是FileIO资源。请注意,PP_ERROR_FILENOTFOUND在尝试删除不存在的文件时,会检查结果 以提供更好的错误消息:

    int32_t result = ref.Delete(pp::BlockUntilComplete());
    if (result == PP_ERROR_FILENOTFOUND) {
      ShowStatusMessage("File/Directory not found");
      return;
    } else if (result != PP_OK) {
      ShowErrorMessage("Deletion failed", result);
      return;
    }

    列出目录中的文件

    FileIoInstance::ListList Directory按下按钮时调用。与所有其他操作一样,它会检查FileSystem是否已打开并创建新的FileRef

    if (!file_system_ready_) {
      ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
      return;
    }
    
    pp::FileRef ref(file_system_, dir_name.c_str());

    与其他操作不同,它不会进行阻塞调用 pp::FileRef::ReadDirectoryEntries。由于ReadDirectoryEntries在其回调中返回结果目录条目,因此创建了一个指向的新回调对象FileIoInstance::ListCallback

    pp::CompletionCallbackFactory模板类用于实例化一个新的回调。请注意,FileRef资源作为参数传递; 这将向回调对象添加引用计数,以防止FileRef 在函数完成时销毁资源。

    // Pass ref along to keep it alive.
    ref.ReadDirectoryEntries(callback_factory_.NewCallbackWithOutput(
        &FileIoInstance::ListCallback, ref));

    FileIoInstance::ListCallback然后得到作为传递的结果 std::vectorpp::DirectoryEntry对象,并将其发送给JavaScript:

    void ListCallback(int32_t result,
                      const std::vector<pp::DirectoryEntry>& entries,
                      pp::FileRef /*unused_ref*/) {
      if (result != PP_OK) {
        ShowErrorMessage("List failed", result);
        return;
      }
    
      std::stringstream ss;
      ss << "LIST";
      for (size_t i = 0; i < entries.size(); ++i) {
        pp::Var name = entries[i].file_ref().GetName();
        if (name.is_string()) {
          ss << "|" << name.AsString();
        }
      }
      PostMessage(ss.str());
      ShowStatusMessage("List success");
    }

    创建一个新目录

    FileIoInstance::MakeDirMake Directory按下按钮时调用。与所有其他操作一样,它会检查FileSystem是否已打开并创建新的FileRef

    if (!file_system_ready_) {
      ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
      return;
    }
    pp::FileRef ref(file_system_, dir_name.c_str());

    然后pp::FileRef::MakeDirectory调用该函数。

    int32_t result = ref.MakeDirectory(
        PP_MAKEDIRECTORYFLAG_NONE, pp::BlockUntilComplete());
    if (result != PP_OK) {
      ShowErrorMessage("Make directory failed", result);
      return;
    }
    ShowStatusMessage("Make directory success");

    CC-By 3.0许可下提供的内容

  • 相关阅读:
    关于正无穷大取值小记
    Ubuntu16.04的图形化界面无法启动问题
    腾讯地图 API 调用入门
    背包九讲PDF
    剑指offer 题解记录
    C++ 各类型转换及关键字
    简易web服务器
    树 总结
    排序算法总结
    C++进阶知识整理
  • 原文地址:https://www.cnblogs.com/SunkingYang/p/11049129.html
Copyright © 2011-2022 走看看