Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / docs / templates / articles / app_codelab_filesystem.html
blob483b4299ec0affa6c8d16f153dab6d8767b31343
1 <h1 id="export-to-filesystem">
2 <span class="h1-step">Step 6:</span>
3 Export Todos to the Filesystem
4 </h1>
6 <p class="note">
7 <strong>Want to start fresh from here?</strong>
8 Find the previous step's code in the <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step5</strong></em>.
9 </p>
11 <p>In this step, you will learn:</p>
13 <ul>
14 <li>How to get a reference to a file in the external filesystem.</li>
15 <li>How to write to the filesystem.</li>
16 </ul>
18 <p>
19 <em>Estimated time to complete this step: 20 minutes.</em>
20 <br>
21 To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page &#8595;</a>.
22 </p>
24 <h2 id="export-todos">Export todos</h2>
26 <p>This step adds an export button to the app.
27 When clicked, the current todo items are saved to a text file selected by the user.
28 If the file exists, it's replaced. Otherwise, a new file gets created.</p>
30 <h3 id="update-permissions">Update permissions</h3>
32 <p>File system permissions can be requested as a string for read-only access, or an Object with additional properties. For example:</p>
34 <pre>
35 // Read only
36 "permissions": [<b>"fileSystem"</b>]
38 // Read and write
39 "permissions": [<b>{"fileSystem": ["write"]}</b>]
41 // Read, write, autocomplate previous input, and select folder directories instead of files
42 "permissions": [<b>{"fileSystem": ["write", "retainEntries", "directory"]}</b>]
43 </pre>
45 <p>You need read and write access.
46 In <em>manifest.json</em>, request the <code>{fileSystem: [ "write" ] }</code> permission:</p>
48 <pre data-filename="manifest.json">
49 "permissions": ["storage", "alarms", "notifications", "webview",
50 "&lt;all_urls&gt;"<b>, { "fileSystem": ["write"] }</b> ],
51 </pre>
54 <h3 id="update-html-view">Update HTML view</h3>
56 <p>In <em>index.html</em>, add an <b>Export to disk</b> button and
57 a <code>div</code> where the app shows a status message:</p>
59 <pre data-filename="index.html">
60 &lt;footer id="info"&gt;
61 &lt;button id="toggleAlarm"&gt;Activate alarm&lt;/button&gt;
62 <b>&lt;button id="exportToDisk"&gt;Export to disk&lt;/button&gt;</b>
63 <b>&lt;div id="status"&gt;&lt;/div&gt;</b>
64 ...
65 &lt;/footer&gt;
66 </pre>
68 <p>Also in <em>index.html</em>, load the <em>export.js</em> script:</p>
70 <pre data-filename="index.html">
71 ...
72 &lt;script src="js/alarms.js"&gt;&lt;/script&gt;
73 <b>&lt;script src="js/export.js"&gt;&lt;/script&gt;</b>
74 </pre>
76 <h3 id="create-js">Create export script</h3>
78 <p>Create a new JavaScript file named <em>export.js</em> using the code below.
79 Save it in the <em>js</em> folder.</p>
81 <pre data-filename="export.js">
82 (function() {
84 var dbName = 'todos-vanillajs';
86 var savedFileEntry, fileDisplayPath;
88 function getTodosAsText(callback) {
91 function exportToFileEntry(fileEntry) {
94 function doExportToDisk() {
97 document.getElementById('exportToDisk').addEventListener('click', doExportToDisk);
99 })();
100 </pre>
102 <p>Right now, <em>export.js</em> only contains a click listener on the <strong>Export to disk</strong> button and stubs for <code>getTodosAsText()</code>, <code>exportToFileEntry</code>, and <code>doExportToDisk()</code>.</p>
104 <h3 id="get-todos-as-text">Get todo items as text</h3>
106 <p>Update <code>getTodosAsText()</code> so that it reads todos from <code>chrome.storage.local</code> and generates a textual representation of them:</p>
108 <pre data-filename="export.js">
109 function getTodosAsText(callback) {
110 <b> chrome.storage.local.get(dbName, function(storedData) {
111 var text = '';
113 if ( storedData[dbName].todos ) {
114 storedData[dbName].todos.forEach(function(todo) {
115 text += '- ';
116 if ( todo.completed ) {
117 text += '[DONE] ';
119 text += todo.title;
120 text += '\n';
121 }, '');
124 callback(text);
126 }.bind(this));</b>
128 </pre>
130 <h3 id="choose-file">Choose a file</h3>
132 <p>Update <code>doExportToDisk()</code> with <code><a href="/apps/fileSystem#method-chooseEntry">chrome.fileSystem.chooseEntry()</a></code> to allow the user to choose a file:</p>
134 <pre data-filename="export.js">
135 function doExportToDisk() {
137 <b> if (savedFileEntry) {
139 exportToFileEntry(savedFileEntry);
141 } else {
143 chrome.fileSystem.chooseEntry( {
144 type: 'saveFile',
145 suggestedName: 'todos.txt',
146 accepts: [ { description: 'Text files (*.txt)',
147 extensions: ['txt']} ],
148 acceptsAllTypes: true
149 }, exportToFileEntry);
151 }</b>
153 </pre>
155 <p>The first parameter of <code>chrome.fileSystem.chooseEntry()</code> is an object of options. The second parameter is a callback method.</p>
157 <p>If there's already a saved <code>FileEntry</code>,
158 use that instead when calling <code>exportToFileEntry()</code>.
159 File references exist for the lifetime of the object representing the <code>FileEntry</code>.
160 This example ties <code>FileEntry</code> to the app window so the JavaScript code can write
161 to the selected file without any user interaction as long as the app window remains open.</p>
163 <h3 id="use-fileentry">Use FileEntry to write todos items to disk</h3>
165 <p>Update <code>exportToFileEntry()</code> to save the todos as text via the <code>FileEntry</code> Web API:</p>
167 <pre data-filename="export.js">
168 function exportToFileEntry(fileEntry) {
169 <b> savedFileEntry = fileEntry;
171 var status = document.getElementById('status');
173 // Use this to get a file path appropriate for displaying
174 chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
175 fileDisplayPath = path;
176 status.innerText = 'Exporting to '+path;
179 getTodosAsText( function(contents) {
181 fileEntry.createWriter(function(fileWriter) {
183 var truncated = false;
184 var blob = new Blob([contents]);
186 fileWriter.onwriteend = function(e) {
187 if (!truncated) {
188 truncated = true;
189 // You need to explicitly set the file size to truncate
190 // any content that might have been there before
191 this.truncate(blob.size);
192 return;
194 status.innerText = 'Export to '+fileDisplayPath+' completed';
197 fileWriter.onerror = function(e) {
198 status.innerText = 'Export failed: '+e.toString();
201 fileWriter.write(blob);
204 });</b>
206 </pre>
208 <p><code><a href="/apps/fileSystem#method-getDisplayPath">chrome.fileSystem.getDisplayPath()</a></code> gets a displayable file path that outputs to the status <code>div</code>.</p>
210 <p>Use <code>fileEntry.createWriter()</code> to create a <code>FileWriter</code> object. <code>fileWriter.write()</code> can then write a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a> to the filesystem. Use <code>fileWriter.onwriteend()</code> and <code>fileWriter.onerror()</code> to update the status <code>div</code>.</p>
212 <!-- <code>fileWriter.truncate()</code> -->
214 <p>For more information about <code>FileEntry</code>, read <a href="http://www.html5rocks.com/en/tutorials/file/filesystem/"><em>Exploring the FileSystem APIs</em></a> on HTML5Rocks, or refer to the <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/FileEntry">FileEntry docs</a></code> on MDN.</p>
216 <h3 id="persistance">Persist FileEntry objects</h3>
218 <p><strong>Advanced</strong>: <code>FileEntry</code> objects cannot be persisted indefinitely. Your app needs to ask the user to choose a file every time the app is launched.
219 If your app was forced to restart due to a runtime crash or update,
220 <a href="/apps/fileSystem#method-restoreEntry">restoreEntry()</a>
221 is an option to restore a <code>FileEntry</code>.</p>
223 <p>If you wish, experiment by saving the ID returned by
224 <a href="/apps/fileSystem.html#method-retainEntry">retainEntry()</a>
225 and restoring it on app restart. (Hint: Add a listener to the <code>onRestarted</code> event
226 in the background page.)</p>
228 <h2 id="launch">Launch your finished Todo app</h2>
230 <p>You are done Step 6! Reload your app and add some todos. Click <b>Export to disk</b> to export your todos to a .txt file.</p>
232 <figure>
233 <img src="{{static}}/images/app_codelab/step6-completed.png" alt="The Todo app with exported todos"/>
234 </figure>
236 <h2 id="recap">For more information</h2>
238 <p>For more detailed information about some of the APIs introduced in this step, refer to:</p>
240 <ul>
241 <li>
242 <a href="/apps/app_storage#filesystem" title="Read 'Using the Chrome Filesystem API' in the Chrome developer docs">Using the Chrome Filesystem API</a>
243 <a href="#export-todos" class="anchor-link-icon" title="This feature mentioned in 'Export todos'">&#8593;</a>
244 </li>
245 <li>
246 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a>
247 <a href="#update-permissions" class="anchor-link-icon" title="This feature mentioned in 'Update permissions'">&#8593;</a>
248 </li>
249 <li>
250 <a href="/apps/storage#method-StorageArea-get" title="Read 'chrome.storage.local.get()' in the Chrome developer docs">chrome.storage.local.get()</a>
251 <a href="#get-todos-as-text" class="anchor-link-icon" title="This feature mentioned in 'Get todo items as text'">&#8593;</a>
252 </li>
253 <li>
254 <a href="/apps/fileSystem#method-chooseEntry" title="Read 'chrome.fileSystem.chooseEntry()' in the Chrome developer docs">chrome.fileSystem.chooseEntry()</a>
255 <a href="#choose-file" class="anchor-link-icon" title="This feature mentioned in 'Choose a file'">&#8593;</a>
256 </li>
257 <li>
258 <a href="/apps/fileSystem#method-getDisplayPath" title="Read 'chrome.fileSystem.getDisplayPath()' in the Chrome developer docs">chrome.fileSystem.getDisplayPath()</a>
259 <a href="#use-fileentry" class="anchor-link-icon" title="This feature mentioned in 'Use FileEntry to write todos items to disk'">&#8593;</a>
260 </li>
261 <li>
262 <a href="/apps/fileSystem#method-restoreEntry" title="Read 'chrome.fileSystem.restoreEntry()' in the Chrome developer docs">chrome.fileSystem.restoreEntry()</a>
263 <a href="#persistance" class="anchor-link-icon" title="This feature mentioned in 'Persist FileEntry objects'">&#8593;</a>
264 </li>
265 <li>
266 <a href="/apps/fileSystem#method-retainEntry" title="Read 'chrome.fileSystem.retainEntry()' in the Chrome developer docs">chrome.fileSystem.retainEntry()</a>
267 <a href="#persistance" class="anchor-link-icon" title="This feature mentioned in 'Persist FileEntry objects'">&#8593;</a>
268 </li>
269 </ul>
271 <p>Ready to continue onto the next step? Go to <a href="app_codelab_publish.html">Step 7 - Publish your app &raquo;</a></p>