Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / docs / templates / articles / app_storage.html
blobcdd229ebb889a8eea96f8f5ec2e17b25aa28f85d
1 <h1>Storage APIs</h1>
3 <p>
4 Almost every aspect of app development involves some element
5 of sending or receiving data.
6 Starting with the basics,
7 you should use an MVC framework to help you design and implement your app
8 so that data is completely separate from the app's view on that data
9 (see <a href="app_frameworks">MVC Architecture</a>).
10 </p>
12 <p>
13 You also need to think about how data is handled when your app is offline
14 (see <a href="offline_apps">Offline First</a>).
15 This doc briefly introduces the storage options
16 for sending, receiving, and saving data locally;
17 the remainder of the doc shows you how
18 to use Chrome's File System and Sync File System APIs
19 (see also <a href="fileSystem">fileSystem API</a> and
20 <a href="syncFileSystem">syncFileSystem API</a>).
21 </p>
23 <p class="note">
24 <b>API Samples: </b>
25 Want to play with the code?
26 Check out the
27 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/filesystem-access">filesystem-access</a>,
28 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/syncfs-editor">syncfs-editor</a>
29 and <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/storage">storage</a> samples.
30 </p>
32 <h2 id="options">Storage options</h2>
34 <p>
35 Packaged apps use many different mechanisms
36 to send and receive data.
37 For external data (resources, web pages),
38 you need to be aware of the
39 <a href="contentSecurityPolicy">Content Security Policy (CSP)</a>.
40 Similar to Chrome Extensions,
41 you can use
42 <a href="app_external#external">cross-origin XMLHttpRequests</a>
43 to communicate with remote servers.
44 You can also isolate external pages,
45 so that the rest of your app is secure
46 (see <a href="app_external#webview">Embed external web pages</a>).
47 </p>
49 <p>
50 When saving data locally,
51 you can use the <a href="storage">Chrome Storage API</a>
52 to save small amounts of string data and
53 IndexedDB to save structured data.
54 With IndexedDB, you can persist JavaScript objects
55 to an object store and use the store's indexes to query data
56 (to learn more, see HTML5 Rock's
57 <a href="http://www.html5rocks.com/tutorials/indexeddb/todo/">Simple Todo List Tutorial</a>).
58 For all other types of data,
59 like binary data,
60 use the Filesystem and Sync Filesystem APIs.
61 </p>
63 <p>
64 Chrome's Filesystem and Sync Filesystem APIs extend the
65 <a href="http://www.html5rocks.com/tutorials/file/filesystem/">HTML5 FileSystem API</a>.
66 With Chrome's Filesystem API,
67 apps can create, read, navigate,
68 and write to a sandboxed section
69 of the user's local file system.
70 For example,
71 a photo-sharing app can use the Filesystem API
72 to read and write any photos that a user selects.
73 </p>
75 <p>
76 With Chrome's Sync Filesystem API,
77 apps can save and synchronize data
78 on a user's Google Drive
79 so that the same data can be available across different clients.
80 For example, a
81 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/syncfs-editor">cloud-backed text editor</a>
82 app can automatically sync new text files to a user's Google Drive account.
83 When the user opens the text editor in a new client,
84 Google Drive pushes new text files to that instance of the text editor.
85 </p>
87 <p>
88 Note: Unlike regular Filesystem API, Chrome's Sync Filesystem API
89 currently does <b>NOT</b> support directory operations, except for
90 reading directory entries in the root directory.
91 An attempt to create a directory in Sync Filesystem will result
92 in INVALID_MODIFICATION_ERROR.
93 </p>
95 <h2 id="filesystem">Using the Chrome Filesystem API</h2>
96 <h3 id="filesystem-manifest">Adding file system permission</h3>
98 <p>
99 To use Chrome's File System API,
100 you need to add the "fileSystem" permission to the manifest,
101 so that you can obtain permission from the user
102 to store persistent data.
104 <pre data-filename="manifest.json">
105 "permissions": [
106 "...",
107 "fileSystem"
109 </pre>
111 <h3 id="import">User-options for selecting files</h3>
114 Users expect to select files
115 in the same way they always do.
116 At a minimum,
117 they expect a 'choose file' button
118 and standard file-chooser.
119 If your app makes heavy use of file-handing,
120 you should also implement drag-and-drop
121 (see below and also check out
122 <a href="http://www.html5rocks.com/tutorials/dnd/basics/">Native HTML5 Drag and Drop</a>).
123 </p>
125 <h3 id="path">Obtaining the path of a fileEntry</h3>
128 To get the full path
129 of the file the user selected,
130 <code>fileEntry</code>,
131 call <code>getDisplayPath()</code>:
132 </p>
134 <pre>
135 function displayPath(fileEntry) {
136 chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
137 console.log(path)
140 </pre>
142 <h3 id="drag">Implementing drag-and-drop</h3>
145 If you need to implement drag-and-drop selection,
146 the drag-and-drop file controller
147 (<code>dnd.js</code>) in the
148 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/filesystem-access">filesystem-access</a>
149 sample is a good starting point.
150 The controller creates a file entry
151 from a <code>DataTransferItem</code>
152 via drag-and-drop.
153 In this example,
154 the <code>fileEntry</code> is set
155 to the first dropped item.
156 </p>
158 <pre>
159 var dnd = new DnDFileController('body', function(data) {
160 var fileEntry = data.items[0].webkitGetAsEntry();
161 displayPath(fileEntry);
163 </pre>
165 <h3 id="read">Reading a file</h3>
168 The following code opens the file (read-only) and
169 reads it as text using a <code>FileReader</code> object.
170 If the file doesn't exist, an error is thrown.
171 </p>
173 <pre>
174 var chosenFileEntry = null;
176 chooseFileButton.addEventListener('click', function(e) {
177 chrome.fileSystem.chooseEntry({type: 'openFile'}, function(readOnlyEntry) {
179 readOnlyEntry.file(function(file) {
180 var reader = new FileReader();
182 reader.onerror = errorHandler;
183 reader.onloadend = function(e) {
184 console.log(e.target.result);
187 reader.readAsText(file);
191 </pre>
193 <h3 id="write">Writing a file</h3>
196 The two common use-cases
197 for writing a file are "Save" and "Save as".
198 The following code creates a
199 <code>writableEntry</code>
200 from the read-only <code>chosenFileEntry</code> and
201 writes the selected file to it.
202 </p>
204 <pre>
205 chrome.fileSystem.getWritableEntry(chosenFileEntry, function(writableFileEntry) {
206 writableFileEntry.createWriter(function(writer) {
207 writer.onerror = errorHandler;
208 writer.onwriteend = callback;
210 chosenFileEntry.file(function(file) {
211 writer.write(file);
213 }, errorHandler);
215 </pre>
218 The following code creates a new file
219 with "Save as" functionality and
220 writes the new blob to the file
221 using the <code>writer.write()</code> method.
222 </p>
224 <pre>
225 chrome.fileSystem.chooseEntry({type: 'saveFile'}, function(writableFileEntry) {
226 writableFileEntry.createWriter(function(writer) {
227 writer.onerror = errorHandler;
228 writer.onwriteend = function(e) {
229 console.log('write complete');
231 writer.write(new Blob(['1234567890'], {type: 'text/plain'}));
232 }, errorHandler);
234 </pre>
236 <h2 id="sync-filesystem">Using the Chrome Sync Filesystem API</h2>
239 Using syncable file storage,
240 returned data objects can be operated on in the same way
241 as local offline file systems in the
242 <a href="http://www.w3.org/TR/file-system-api/">FileSystem API</a>,
243 but with the added (and automatic) syncing
244 of that data to Google Drive.
245 </p>
248 <h3 id="sync-manifest">Adding sync file system permission</h3>
251 To use Chrome's Sync Filesystem API,
252 you need to add the "syncFileSystem" permission to the manifest,
253 so that you can obtain permission from the user
254 to store and sync persistent data.
256 <pre data-filename="manifest.json">
257 "permissions": [
258 "...",
259 "syncFileSystem"
261 </pre>
263 <h3 id="initiate">Initiating syncable file storage</h3>
266 To initiate syncable file storage in your app,
267 simply call $(ref:syncFileSystem.requestFileSystem).
268 This method returns a syncable filesystem backed by Google Drive,
269 for example:
270 </p>
272 <pre>
273 chrome.syncFileSystem.requestFileSystem(function (fs) {
274 // FileSystem API should just work on the returned 'fs'.
275 fs.root.getFile('test.txt', {create:true}, getEntryCallback, errorCallback);
277 </pre>
279 <h3 id="file-status">About file sync status</h3>
282 Use $(ref:syncFileSystem.getFileStatus) to get the sync status
283 for a current file:
284 </p>
286 <pre>
287 chrome.syncFileSystem.getFileStatus(entry, function(status) {...});
288 </pre>
291 File sync status values can be one of the following:
292 <code>'synced'</code>, <code>'pending'</code>, or <code>'conflicting'</code>.
293 'Synced' means the file is fully synchronized;
294 there're no pending local changes
295 that haven't been synchronized to Google Drive.
296 There can, however, be pending changes on the Google Drive side
297 that haven't been fetched yet.
298 </p>
301 'Pending' means the file has pending changes
302 not yet synchronized to Google Drive.
303 If the app is running online,
304 local changes are (almost) immediately synchronized to Google Drive, and the
305 $(ref:syncFileSystem.onFileStatusChanged)
306 event is fired with the <code>'synced'</code> status
307 (see below for more details).
308 </p>
311 The $(ref:syncFileSystem.onFileStatusChanged)
312 is fired when a file's status changes to <code>'conflicting'</code>.
313 'Conflicting' means there are conflicting changes
314 on both the local storage and Google Drive.
315 A file can be in this state only if the conflict resolution policy
316 is set to <code>'manual'</code>. The default policy is
317 <code>'last_write_win'</code> and conflicts are automatically resolved
318 by simple last-write-win policy. The system's conflict resolution policy
319 can be changed by $(ref:syncFileSystem.setConflictResolutionPolicy).
320 </p>
323 If the conflict resolution policy is set to <code>'manual'</code> and
324 a file results in <code>'conflicting'</code> state, the app can still
325 read and write the file as a local offline file, but the changes are not
326 sync'ed and the file will be kept detached from remote changes made on
327 other clients until the conflict is resolved.
328 The easiest way to resolve a conflict is to delete or rename the local
329 version of file. This forces the remote version to be synchronized,
330 the conflicting state is resolved, and
331 the <code>onFileStatusChanged</code> event is fired
332 with the <code>'synced'</code> status.
333 </p>
335 <h3 id="file-status">Listening for changes in synced status</h3>
339 $(ref:syncFileSystem.onFileStatusChanged)
340 event is fired when the sync status of a file changes.
341 For example,
342 assume a file has pending changes and is in the 'pending' state.
343 The app may have been in offline state so that
344 the change is about to be synchronized.
345 When the sync service detects the pending local change
346 and uploads the change to Google Drive,
347 the service fires the <code>onFileStatusChanged</code> event
348 with following values:
349 <code>{ fileEntry:a fileEntry for the file, status: 'synced', action: 'updated',
350 direction: 'local_to_remote' }</code>.
351 </p>
354 Similarly, regardless of the local activities,
355 the sync service may detect remote changes made by another client,
356 and downloads the changes from Google Drive to the local storage.
357 If the remote change was for adding a new file,
358 an event with following values is fired:
359 <code>{ fileEntry: a fileEntry for the file, status: 'synced', action: 'added',
360 direction: 'remote_to_local' }</code>.
361 </p>
364 If both the local and remote side have conflicting changes for the same file
365 and if the conflict resolution policy is set to <code>'manual'</code>,
366 the file status is changed to <code>conflicting</code> state,
367 is detached from the sync service,
368 and won't be synchronized until the conflict is resolved.
369 In this case an event with following values is fired:
370 <code>{ fileEntry: a fileEntry for the file, status: 'conflicting', action: null,
371 direction: null }</code>.
372 </p>
375 You can add a listener for this event
376 that responds to any changes in status.
377 For example,
378 the <a href="https://github.com/agektmr/ChromeMusicPlayer">Chrome Music Player</a> app
379 listens for any new music synced from Google Drive,
380 but not yet imported to the user's local storage on a particular client.
381 Any music found gets synced to that client:
382 </p>
384 <pre>
385 chrome.syncFileSystem.onFileStatusChanged.addListener(function(fileInfo) {
386 if (fileInfo.status === 'synced') {
387 if (fileInfo.direction === 'remote_to_local') {
388 if (fileInfo.action === 'added') {
389 db.add(fileInfo.fileEntry);
390 } else if (fileInfo.action === 'deleted') {
391 db.remove(fileInfo.fileEntry);
396 </pre>
398 <h3 id="check-usage">Checking API usage</h3>
401 To check the amount of data being used by the API,
402 query the app's local sandboxed directory
403 or the usage bytes returned by
404 $(ref:syncFileSystem.getUsageAndQuota):
405 </p>
407 <pre>
408 chrome.syncFileSystem.getUsageAndQuota(fileSystem, function (storageInfo) {
409 updateUsageInfo(storageInfo.usageBytes);
410 updateQuotaInfo(storageInfo.quotaBytes);
412 </pre>
415 You can also look at the user's sync backend service storage (in Google Drive).
416 Synced files are saved in a hidden Google Drive folder,
417 <strong>Chrome Syncable FileSystem</strong>. The folder won't be shown in
418 your 'My Drive' list but can be accessed by searching the folder name
419 in the search box. (Note that the remote folder layout is
420 <strong>not</strong> guaranteed to remain backwards compatible between
421 releases.)
422 </p>
424 <p class="backtotop"><a href="#top">Back to top</a></p>