1 .. _devguide-coding-fileio:
15 This section describes how to use the `FileIO API
16 </native-client/pepper_stable/cpp/classpp_1_1_file_i_o>`_ to read and write
17 files using a local secure data store.
19 You might use the File IO API with the URL Loading APIs to create an overall
20 data download and caching solution for your NaCl applications. For example:
22 #. Use the File IO APIs to check the local disk to see if a file exists that
24 #. If the file exists locally, load it into memory using the File IO API. If
25 the file doesn't exist locally, use the URL Loading API to retrieve the
27 #. Use the File IO API to write the file to disk.
28 #. Load the file into memory using the File IO API when needed by your
31 The example discussed in this section is included in the SDK in the directory
32 ``examples/api/file_io``.
37 For reference information related to FileIO, see the following documentation:
39 * `file_io.h </native-client/pepper_stable/cpp/file__io_8h>`_ - API to create a
41 * `file_ref.h </native-client/pepper_stable/cpp/file__ref_8h>`_ - API to create
42 a file reference or "weak pointer" to a file in a file system
43 * `file_system.h </native-client/pepper_stable/cpp/file__system_8h>`_ - API to
44 create a file system associated with a file
49 Chrome provides an obfuscated, restricted area on disk to which a web app can
50 safely `read and write files
51 <https://developers.google.com/chrome/whitepapers/storage#persistent>`_. The
52 Pepper FileIO, FileRef, and FileSystem APIs (collectively called the File IO
53 APIs) allow you to access this sandboxed local disk so you can read and write
54 files and manage caching yourself. The data is persistent between launches of
55 Chrome, and is not removed unless your application deletes it or the user
56 manually deletes it. There is no limit to the amount of local data you can
57 use, other than the actual space available on the local drive.
60 .. _enabling_file_access:
62 Enabling local file I/O
63 -----------------------
65 The easiest way to enable the writing of persistent local data is to include
66 the `unlimitedStorage permission
67 </extensions/declare_permissions#unlimitedStorage>`_ in your Chrome Web Store
68 manifest file. With this permission you can use the Pepper FileIO API without
69 the need to request disk space at run time. When the user installs the app
70 Chrome displays a message announcing that the app writes to the local disk.
72 If you do not use the ``unlimitedStorage`` permission you must include
73 JavaScript code that calls the `HTML5 Quota Management API
74 <http://updates.html5rocks.com/2011/11/Quota-Management-API-Fast-Facts>`_ to
75 explicitly request local disk space before using the FileIO API. In this case
76 Chrome will prompt the user to accept a requestQuota call every time one is
79 Testing local file I/O
80 ----------------------
82 You should be aware that using the ``unlimitedStorage`` manifest permission
83 constrains the way you can test your app. Three of the four techniques
84 described in :doc:`Running Native Client Applications <../devcycle/running>`
85 read the Chrome Web Store manifest file and enable the ``unlimitedStorage``
86 permission when it appears, but the first technique (local server) does not.
87 If you want to test the file IO portion of your app with a simple local server,
88 you need to include JavaScript code that calls the HTML5 Quota Management API.
89 When you deliver your application you can replace this code with the
90 ``unlimitedStorage`` manifest permission.
92 The ``file_io`` example
93 =======================
95 The Native Client SDK includes an example, ``file_io``, that demonstrates how
96 to read and write a local disk file. Since you will probably run the example
97 from a local server without a Chrome Web Store manifest file, the example's
98 index file uses JavaScript to perform the Quota Management setup as described
99 above. The example has these primary files:
101 * ``index.html`` - The HTML code that launches the Native Client module and
102 displays the user interface.
103 * ``example.js`` - JavaScript code that requests quota (as described above). It
104 also listens for user interaction with the user interface, and forwards the
105 requests to the Native Client module.
106 * ``file_io.cc`` - The code that sets up and provides an entry point to the
107 Native Client module.
109 The remainder of this section covers the code in the ``file_io.cc`` file for
110 reading and writing files.
115 Like many Pepper APIs, the File IO API includes a set of methods that execute
116 asynchronously and that invoke callback functions in your Native Client module.
117 Unlike most other examples, the ``file_io`` example also demonstrates how to
118 make Pepper calls synchronously on a worker thread.
120 It is illegal to make blocking calls to Pepper on the module's main thread.
121 This restriction is lifted when running on a worker thread---this is called
122 "calling Pepper off the main thread". This often simplifies the logic of your
123 code; multiple asynchronous Pepper functions can be called from one function on
124 your worker thread, so you can use the stack and standard control flow
127 The high-level flow for the ``file_io`` example is described below. Note that
128 methods in the namespace ``pp`` are part of the Pepper C++ API.
130 Creating and writing a file
131 ---------------------------
133 Following are the high-level steps involved in creating and writing to a
136 #. ``pp::FileIO::Open`` is called with the ``PP_FILEOPEN_FLAG_CREATE`` flag to
137 create a file. Because the callback function is ``pp::BlockUntilComplete``,
138 this thread is blocked until ``Open`` succeeds or fails.
139 #. ``pp::FileIO::Write`` is called to write the contents. Again, the thread is
140 blocked until the call to ``Write`` completes. If there is more data to
141 write, ``Write`` is called again.
142 #. When there is no more data to write, call ``pp::FileIO::Flush``.
144 Opening and reading a file
145 --------------------------
147 Following are the high-level steps involved in opening and reading a file:
149 #. ``pp::FileIO::Open`` is called to open the file. Because the callback
150 function is ``pp::BlockUntilComplete``, this thread is blocked until Open
152 #. ``pp::FileIO::Query`` is called to query information about the file, such as
153 its file size. The thread is blocked until ``Query`` completes.
154 #. ``pp::FileIO::Read`` is called to read the contents. The thread is blocked
155 until ``Read`` completes. If there is more data to read, ``Read`` is called
161 Deleting a file is straightforward: call ``pp::FileRef::Delete``. The thread is
162 blocked until ``Delete`` completes.
167 Making a directory is also straightforward: call ``pp::File::MakeDirectory``.
168 The thread is blocked until ``MakeDirectory`` completes.
170 Listing the contents of a directory
171 -----------------------------------
173 Following are the high-level steps involved in listing a directory:
175 #. ``pp::FileRef::ReadDirectoryEntries`` is called, and given a directory entry
176 to list. A callback is given as well; many of the other functions use
177 ``pp::BlockUntilComplete``, but ``ReadDirectoryEntries`` returns results in
178 its callback, so it must be specified.
179 #. When the call to ``ReadDirectoryEntries`` completes, it calls
180 ``ListCallback`` which packages up the results into a string message, and
181 sends it to JavaScript.
183 ``file_io`` deep dive
184 =====================
186 The ``file_io`` example displays a user interface with a couple of fields and
187 several buttons. Following is a screenshot of the ``file_io`` example:
189 .. image:: /images/fileioexample.png
191 Each radio button is a file operation you can perform, with some reasonable
192 default values for filenames. Try typing a message in the large input box and
193 clicking ``Save``, then switching to the ``Load File`` operation, and
196 Let's take a look at what is going on under the hood.
198 Opening a file system and preparing for file I/O
199 ------------------------------------------------
201 ``pp::Instance::Init`` is called when an instance of a module is created. In
202 this example, ``Init`` starts a new thread (via the ``pp::SimpleThread``
203 class), and tells it to open the filesystem:
207 virtual bool Init(uint32_t /*argc*/,
208 const char * /*argn*/ [],
209 const char * /*argv*/ []) {
210 file_thread_.Start();
211 // Open the file system on the file_thread_. Since this is the first
212 // operation we perform there, and because we do everything on the
213 // file_thread_ synchronously, this ensures that the FileSystem is open
214 // before any FileIO operations execute.
215 file_thread_.message_loop().PostWork(
216 callback_factory_.NewCallback(&FileIoInstance::OpenFileSystem));
220 When the file thread starts running, it will call ``OpenFileSystem``. This
221 calls ``pp::FileSystem::Open`` and blocks the file thread until the function
227 Note that the call to ``pp::FileSystem::Open`` uses
228 ``pp::BlockUntilComplete`` as its callback. This is only possible because we
229 are running off the main thread; if you try to make a blocking call from the
230 main thread, the function will return the error
231 ``PP_ERROR_BLOCKS_MAIN_THREAD``.
235 void OpenFileSystem(int32_t /*result*/) {
236 int32_t rv = file_system_.Open(1024 * 1024, pp::BlockUntilComplete());
238 file_system_ready_ = true;
239 // Notify the user interface that we're ready
240 PostMessage("READY|");
242 ShowErrorMessage("Failed to open file system", rv);
246 Handling messages from JavaScript
247 ---------------------------------
249 When you click the ``Save`` button, JavaScript posts a message to the NaCl
250 module with the file operation to perform sent as a string (See :doc:`Messaging
251 System <message-system>` for more details on message passing). The string is
252 parsed by ``HandleMessage``, and new work is added to the file thread:
256 virtual void HandleMessage(const pp::Var& var_message) {
257 if (!var_message.is_string())
260 // Parse message into: instruction file_name_length file_name [file_text]
261 std::string message = var_message.AsString();
262 std::string instruction;
263 std::string file_name;
264 std::stringstream reader(message);
265 int file_name_length;
267 reader >> instruction >> file_name_length;
268 file_name.resize(file_name_length);
269 reader.ignore(1); // Eat the delimiter
270 reader.read(&file_name[0], file_name_length);
274 // Dispatch the instruction
275 if (instruction == kLoadPrefix) {
276 file_thread_.message_loop().PostWork(
277 callback_factory_.NewCallback(&FileIoInstance::Load, file_name));
278 } else if (instruction == kSavePrefix) {
286 ``FileIoInstance::Save`` is called when the ``Save`` button is pressed. First,
287 it checks to see that the FileSystem has been successfully opened:
291 if (!file_system_ready_) {
292 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
296 It then creates a ``pp::FileRef`` resource with the name of the file. A
297 ``FileRef`` resource is a weak reference to a file in the FileSystem; that is,
298 a file can still be deleted even if there are outstanding ``FileRef``
303 pp::FileRef ref(file_system_, file_name.c_str());
305 Next, a ``pp::FileIO`` resource is created and opened. The call to
306 ``pp::FileIO::Open`` passes ``PP_FILEOPEFLAG_WRITE`` to open the file for
307 writing, ``PP_FILEOPENFLAG_CREATE`` to create a new file if it doesn't already
308 exist and ``PP_FILEOPENFLAG_TRUNCATE`` to clear the file of any previous
313 pp::FileIO file(this);
315 int32_t open_result =
317 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
318 PP_FILEOPENFLAG_TRUNCATE,
319 pp::BlockUntilComplete());
320 if (open_result != PP_OK) {
321 ShowErrorMessage("File open for write failed", open_result);
325 Now that the file is opened, it is written to in chunks. In an asynchronous
326 model, this would require writing a separate function, storing the current
327 state on the free store and a chain of callbacks. Because this function is
328 called off the main thread, ``pp::FileIO::Write`` can be called synchronously
329 and a conventional do/while loop can be used:
334 int32_t bytes_written = 0;
336 bytes_written = file.Write(offset,
337 file_contents.data() + offset,
338 file_contents.length(),
339 pp::BlockUntilComplete());
340 if (bytes_written > 0) {
341 offset += bytes_written;
343 ShowErrorMessage("File write failed", bytes_written);
346 } while (bytes_written < static_cast<int64_t>(file_contents.length()));
348 Finally, the file is flushed to push all changes to disk:
352 int32_t flush_result = file.Flush(pp::BlockUntilComplete());
353 if (flush_result != PP_OK) {
354 ShowErrorMessage("File fail to flush", flush_result);
361 ``FileIoInstance::Load`` is called when the ``Load`` button is pressed. Like
362 the ``Save`` function, ``Load`` first checks to see if the FileSystem has been
363 successfully opened, and creates a new ``FileRef``:
367 if (!file_system_ready_) {
368 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
371 pp::FileRef ref(file_system_, file_name.c_str());
373 Next, ``Load`` creates and opens a new ``FileIO`` resource, passing
374 ``PP_FILEOPENFLAG_READ`` to open the file for reading. The result is compared
375 to ``PP_ERROR_FILENOTFOUND`` to give a better error message when the file
380 int32_t open_result =
381 file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete());
382 if (open_result == PP_ERROR_FILENOTFOUND) {
383 ShowErrorMessage("File not found", open_result);
385 } else if (open_result != PP_OK) {
386 ShowErrorMessage("File open for read failed", open_result);
390 Then ``Load`` calls ``pp::FileIO::Query`` to get metadata about the file, such
391 as its size. This is used to allocate a ``std::vector`` buffer that holds the
392 data from the file in memory:
396 int32_t query_result = file.Query(&info, pp::BlockUntilComplete());
397 if (query_result != PP_OK) {
398 ShowErrorMessage("File query failed", query_result);
404 std::vector<char> data(info.size);
406 Similar to ``Save``, a conventional while loop is used to read the file into
407 the newly allocated buffer:
412 int32_t bytes_read = 0;
413 int32_t bytes_to_read = info.size;
414 while (bytes_to_read > 0) {
415 bytes_read = file.Read(offset,
417 data.size() - offset,
418 pp::BlockUntilComplete());
419 if (bytes_read > 0) {
420 offset += bytes_read;
421 bytes_to_read -= bytes_read;
422 } else if (bytes_read < 0) {
423 // If bytes_read < PP_OK then it indicates the error code.
424 ShowErrorMessage("File read failed", bytes_read);
429 Finally, the contents of the file are sent back to JavaScript, to be displayed
430 on the page. This example uses "``DISP|``" as a prefix command for display
435 std::string string_data(data.begin(), data.end());
436 PostMessage("DISP|" + string_data);
437 ShowStatusMessage("Load success");
442 ``FileIoInstance::Delete`` is called when the ``Delete`` button is pressed.
443 First, it checks whether the FileSystem has been opened, and creates a new
448 if (!file_system_ready_) {
449 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
452 pp::FileRef ref(file_system_, file_name.c_str());
454 Unlike ``Save`` and ``Load``, ``Delete`` is called on the ``FileRef`` resource,
455 not a ``FileIO`` resource. Note that the result is checked for
456 ``PP_ERROR_FILENOTFOUND`` to give a better error message when trying to delete
461 int32_t result = ref.Delete(pp::BlockUntilComplete());
462 if (result == PP_ERROR_FILENOTFOUND) {
463 ShowStatusMessage("File/Directory not found");
465 } else if (result != PP_OK) {
466 ShowErrorMessage("Deletion failed", result);
470 Listing files in a directory
471 ----------------------------
473 ``FileIoInstance::List`` is called when the ``List Directory`` button is
474 pressed. Like all other operations, it checks whether the FileSystem has been
475 opened and creates a new ``FileRef``:
479 if (!file_system_ready_) {
480 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
484 pp::FileRef ref(file_system_, dir_name.c_str());
486 Unlike the other operations, it does not make a blocking call to
487 ``pp::FileRef::ReadDirectoryEntries``. Since ``ReadDirectoryEntries`` returns
488 the resulting directory entries in its callback, a new callback object is
489 created pointing to ``FileIoInstance::ListCallback``.
491 The ``pp::CompletionCallbackFactory`` template class is used to instantiate a
492 new callback. Notice that the ``FileRef`` resource is passed as a parameter;
493 this will add a reference count to the callback object, to keep the ``FileRef``
494 resource from being destroyed when the function finishes.
498 // Pass ref along to keep it alive.
499 ref.ReadDirectoryEntries(callback_factory_.NewCallbackWithOutput(
500 &FileIoInstance::ListCallback, ref));
502 ``FileIoInstance::ListCallback`` then gets the results passed as a
503 ``std::vector`` of ``pp::DirectoryEntry`` objects, and sends them to
508 void ListCallback(int32_t result,
509 const std::vector<pp::DirectoryEntry>& entries,
510 pp::FileRef /*unused_ref*/) {
511 if (result != PP_OK) {
512 ShowErrorMessage("List failed", result);
516 std::stringstream ss;
518 for (size_t i = 0; i < entries.size(); ++i) {
519 pp::Var name = entries[i].file_ref().GetName();
520 if (name.is_string()) {
521 ss << "|" << name.AsString();
524 PostMessage(ss.str());
525 ShowStatusMessage("List success");
528 Making a new directory
529 ----------------------
531 ``FileIoInstance::MakeDir`` is called when the ``Make Directory`` button is
532 pressed. Like all other operations, it checks whether the FileSystem has been
533 opened and creates a new ``FileRef``:
537 if (!file_system_ready_) {
538 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
541 pp::FileRef ref(file_system_, dir_name.c_str());
543 Then the ``pp::FileRef::MakeDirectory`` function is called.
547 int32_t result = ref.MakeDirectory(
548 PP_MAKEDIRECTORYFLAG_NONE, pp::BlockUntilComplete());
549 if (result != PP_OK) {
550 ShowErrorMessage("Make directory failed", result);
553 ShowStatusMessage("Make directory success");