In this step, you will learn:
- How to get a reference to a file in the external filesystem.
- How to write to the filesystem.
Estimated time to complete this step: 20 minutes.
To preview what you will complete in this step, jump down to the bottom of this page ↓.
Export todos
This step adds an export button to the app. When clicked, the current todo items are saved to a text file selected by the user. If the file exists, it's replaced. Otherwise, a new file gets created.
Update permissions
File system permissions can be requested as a string for read-only access, or an Object with additional properties. For example:
// Read only
"permissions": ["fileSystem"]
// Read and write
"permissions": [{"fileSystem": ["write"]}]
// Read, write, autocomplate previous input, and select folder directories instead of files
"permissions": [{"fileSystem": ["write", "retainEntries", "directory"]}]
You need read and write access. In manifest.json, request the {fileSystem: [ "write" ] }
permission:
"permissions": [
"storage",
"alarms",
"notifications",
"webview",
"<all_urls>",
{ "fileSystem": ["write"] }
],
Update HTML view
In index.html, add an Export to disk button and a div
where the app shows a status message:
<footer id="info">
<button id="toggleAlarm">Activate alarm</button>
<button id="exportToDisk">Export to disk</button>
<div id="status"></div>
...
</footer>
Also in index.html, load the export.js script:
...
<script src="js/alarms.js"></script>
<script src="js/export.js"></script>
Create export script
Create a new JavaScript file named export.js using the code below. Save it in the js folder.
(function() {
var dbName = 'todos-vanillajs';
var savedFileEntry, fileDisplayPath;
function getTodosAsText(callback) {
}
function exportToFileEntry(fileEntry) {
}
function doExportToDisk() {
}
document.getElementById('exportToDisk').addEventListener('click', doExportToDisk);
})();
Right now, export.js only contains a click listener on the Export to disk button and stubs for
getTodosAsText()
, exportToFileEntry
, and doExportToDisk()
.
Get todo items as text
Update getTodosAsText()
so that it reads todos from chrome.storage.local
and generates a textual
representation of them:
function getTodosAsText(callback) {
chrome.storage.local.get(dbName, function(storedData) {
var text = '';
if ( storedData[dbName].todos ) {
storedData[dbName].todos.forEach(function(todo) {
text += '- ';
if ( todo.completed ) {
text += '[DONE] ';
}
text += todo.title;
text += '\n';
}, '');
}
callback(text);
}.bind(this));
}
Choose a file
Update doExportToDisk()
with chrome.fileSystem.chooseEntry()
to allow the user to choose a
file:
function doExportToDisk() {
if (savedFileEntry) {
exportToFileEntry(savedFileEntry);
} else {
chrome.fileSystem.chooseEntry( {
type: 'saveFile',
suggestedName: 'todos.txt',
accepts: [ { description: 'Text files (*.txt)',
extensions: ['txt']} ],
acceptsAllTypes: true
}, exportToFileEntry);
}
}
The first parameter of chrome.fileSystem.chooseEntry()
is an object of options. The second
parameter is a callback method.
If there's already a saved FileEntry
, use that instead when calling exportToFileEntry()
. File
references exist for the lifetime of the object representing the FileEntry
. This example ties
FileEntry
to the app window so the JavaScript code can write to the selected file without any user
interaction as long as the app window remains open.
Use FileEntry to write todos items to disk
Update exportToFileEntry()
to save the todos as text via the FileEntry
Web API:
function exportToFileEntry(fileEntry) {
savedFileEntry = fileEntry;
var status = document.getElementById('status');
// Use this to get a file path appropriate for displaying
chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
fileDisplayPath = path;
status.innerText = 'Exporting to '+path;
});
getTodosAsText( function(contents) {
fileEntry.createWriter(function(fileWriter) {
var truncated = false;
var blob = new Blob([contents]);
fileWriter.onwriteend = function(e) {
if (!truncated) {
truncated = true;
// You need to explicitly set the file size to truncate
// any content that might have been there before
this.truncate(blob.size);
return;
}
status.innerText = 'Export to '+fileDisplayPath+' completed';
};
fileWriter.onerror = function(e) {
status.innerText = 'Export failed: '+e.toString();
};
fileWriter.write(blob);
});
});
}
chrome.fileSystem.getDisplayPath()
gets a displayable file path that outputs to the status
div
.
Use fileEntry.createWriter()
to create a FileWriter
object. fileWriter.write()
can then write
a Blob to the filesystem. Use fileWriter.onwriteend()
and fileWriter.onerror()
to update
the status div
.
For more information about FileEntry
, read Exploring the FileSystem APIs on HTML5Rocks, or
refer to the FileEntry docs
on MDN.
Persist FileEntry objects
Advanced: FileEntry
objects cannot be persisted indefinitely. Your app needs to ask the user
to choose a file every time the app is launched. If your app was forced to restart due to a runtime
crash or update, restoreEntry() is an option to restore a FileEntry
.
If you wish, experiment by saving the ID returned by retainEntry() and restoring it on app
restart. (Hint: Add a listener to the onRestarted
event in the background page.)
Launch your finished Todo app
You are done Step 6! Reload your app and add some todos. Click Export to disk to export your todos to a .txt file.
For more information
For more detailed information about some of the APIs introduced in this step, refer to:
- Using the Chrome Filesystem API ↑
- Declare Permissions ↑
- chrome.storage.local.get() ↑
- chrome.fileSystem.chooseEntry() ↑
- chrome.fileSystem.getDisplayPath() ↑
- chrome.fileSystem.restoreEntry() ↑
- chrome.fileSystem.retainEntry() ↑
Ready to continue onto the next step? Go to Step 7 - Publish your app »