File IO
This page is part of the extension development documentation project.
Ask your questions in MozillaZine Forums. Also try browsing example code.
Note: development documentation is in process of being moved to Mozilla Development Center (MDC).
This article describes local file input/output in chrome JavaScript.
You access the filesystem using Mozilla XPCOM components. The list of components used for local I/O is available at XulPlanet.com.
Available libraries
There are a few JavaScript wrappers for I/O XPCOM components. See JSLib and io.js (original by MonkeeSage). The io.js module is much smaller and very easy to use (simple examples are included in the module).
Creating a file object ("opening" files)
var file = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); file.initWithPath("/home");
Note, that the path passed to initWithPath()
should be in "native" form (e.g. "C:\\Windows"). If you need to use file:// URIs as initializers, see below.
Also note, that initWithPath()
/ initWithFile()
functions don't throw an exception if specified file does not exist. An exception is thrown when methods that require the file existance are called, e.g. isDirectory()
, moveTo()
etc.
Getting special files
// get profile directory var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile);
Here are some strings you can put in place of "ProfD"
(stolen from MonkeeSage's I/O module comments):
String | Meaning |
---|---|
ProfD | profile directory |
DefProfRt | user (e.g., /root/.mozilla) |
UChrm | %profile%/chrome |
DefRt | %installation%/defaults |
PrfDef | %installation%/defaults/pref |
ProfDefNoLoc | %installation%/defaults/profile |
APlugns | %installation%/plugins |
AChrom | %installation%/chrome |
ComsD | %installation%/components |
CurProcD | installation (usually) |
Home | OS root (e.g., /root) |
TmpD | OS tmp (e.g., /tmp) |
Look in the Source for other strings available: [1] [2].
Getting your extension's folder
- This will only work in Firefox/Thunderbird 1.5, not 1.0.
To get the directory that an extension is installed in, you have to use nsIExtensionManager
like this:
var ext = Components.classes["@mozilla.org/extensions/manager;1"] .getService(Components.interfaces.nsIExtensionManager) .getInstallLocation(ID) .location;
Replace ID with the extension's ID, and this will return an nsIFile
of the directory that the extension is installed in. This value is read only. For additional information, view the source [3].
To get to the folder that the extension is installed in, add this line:
ext.append(ID);
Again, replace ID with the ID of the extension.
Creating Folders
To create a folder, use nsIFile.create()
:
var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile); file.append("DIR"); if( !file.exists() || !file.isDirectory() ) { // if it doesn't exist, create file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0664); }
The above example creates a folder called "DIR" in the Profile folder. For more information refer too the nsIFile Reference at XULPlanet.
Creating temporary files
To create a temporary file, use nsIFile.createUnique()
:
var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("TmpD", Components.interfaces.nsIFile); file.append("suggestedName.tmp"); file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664); // do whatever you need to the created file alert(file.path);
User input via nsIFilePicker
See Dev : nsIFilePicker.
nsIFile and path strings
You can use nsIFile.path
to get platform-specific path string, e.g. "C:\Windows\System32" or "/usr/share".
If you want to get a file:// URL of a file or an nsIFile
from file:// URL, you need to use nsIFileProtocolHandler:
// file is nsIFile var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); var fileHandler = ios.getProtocolHandler("file") .QueryInterface(Components.interfaces.nsIFileProtocolHandler); var URL = fileHandler.getURLSpecFromFile(file);
To load from file://, http://, chrome://, resource:// and other URLs directly, use XMLHttpRequest or nsIChannel (example).
Also note that generally you don't need to use nsIFile::path
. Use nsIFile
directly wherever possible. An example below shows how you should save a path in user prefs.
Storing nsILocalFile in preferences
The following two snippets show the right way to store a file path in user preferences (more about preferences in Mozilla):
Absolute path (nsILocalFile)
To store arbitrary path in user preferences, use this code.
// |file| is nsILocalFile // 1. Write path to prefs var prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService) .getBranch("extensions.myext."); prefs.setComplexValue("filename", Components.interfaces.nsILocalFile, file); // 2. Read path from prefs var file = prefs.getComplexValue("filename", Components.interfaces.nsILocalFile);
Relative path (nsIRelativeFilePref)
To store paths relative to one of the predefined folders listed above, for example file relative to profile folder, use the following code:
// 1. Write to prefs var relFile = Components.classes["@mozilla.org/pref-relativefile;1"] .createInstance(Components.interfaces.nsIRelativeFilePref); relFile.relativeToKey = "ProfD"; // or any other string listed above relFile.file = file; // |file| is nsILocalFile prefs.setComplexValue("filename", Components.interfaces.nsIRelativeFilePref, relFile); // 2. Read from prefs var value = prefs.getComplexValue("filename", Components.interfaces.nsIRelativeFilePref); // |value.file| is the file.
Get a file in given directory
Assume, file
is an nsIFile
pointing to some directory (f.e. a user profile directory). You can use file.append("myfile.txt");
to make file
point to myfile.txt inside that directory.
Note: avoid using dir.path+"\\"+"myfile.txt"
, as it is not cross-platform at all. Using something like ((path.search(/\\/) != -1) ? path + "\\" : path + "/") + "myfile.txt";
is possible, but nsIFile::append()
is much easier to read and is guaranteed to work on all platforms Mozilla itself works.
Enumerating files in given directory
The snippet below makes an array of |nsIFile|
s corresponding to sub-directories/"sub-files" of given directory. You can tell files from folders by calling nsIFile::isDirectory()
and nsIFile::isFile()
methods on each entry
.
// file is the given directory (nsIFile) var entries = file.directoryEntries; var array = []; while(entries.hasMoreElements()) { var entry = entries.getNext(); entry.QueryInterface(Components.interfaces.nsIFile); array.push(entry); }
Reading from a file
Note: This code is not intl-aware. Reading non-ASCII characters will not work correctly. See Reading textual data for intl-aware code.
Simple
// |file| is nsIFile var data = ""; var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"] .createInstance(Components.interfaces.nsIScriptableInputStream); fstream.init(file, 1, 0, false); sstream.init(fstream); var str = sstream.read(-1); while (str.length > 0) { data += str; str = sstream.read(-1); } sstream.close(); fstream.close(); alert(data);
Line by line
// open an input stream from file var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); istream.init(file, 0x01, 0444, 0); istream.QueryInterface(Components.interfaces.nsILineInputStream); // read lines into array var line = {}, lines = [], hasmore; do { hasmore = istream.readLine(line); lines.push(line.value); } while(hasmore); istream.close(); // do something with read data alert(lines);
Asynchronously
This will allow you to read a file without locking up the UI thread.
// |file| is nsIFile var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); var fileURI = ios.newFileURI(file); var channel = ios.newChannelFromURI(fileURI); var observer = { onStreamComplete : function(aLoader, aContext, aStatus, aLength, aResult) { alert(aResult); } }; var sl = Components.classes["@mozilla.org/network/stream-loader;1"] .createInstance(Components.interfaces.nsIStreamLoader); sl.init(channel, observer, null);
Writing to a file
Note: This code is not intl-aware. Writing non-ASCII characters will not work correctly. See Writing textual data for intl-aware code.
// file is nsIFile, data is a string var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance(Components.interfaces.nsIFileOutputStream); // use 0x02 | 0x10 to open file for appending. foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate foStream.write(data, data.length); foStream.close();
Flags parameter to the nsIFileOutputStream::init()
function
(For more information refer to prio.h file).
flags: The file status flags. It is a bitwise OR of the following bit flags (only one of the first three flags below may be used):
Name | Value | Description |
---|---|---|
PR_RDONLY | 0x01 | Open for reading only. |
PR_WRONLY | 0x02 | Open for writing only. |
PR_RDWR | 0x04 | Open for reading and writing. |
PR_CREATE_FILE | 0x08 |
If the file does not exist, the file is created. If the file exists, this flag has no effect. |
PR_APPEND | 0x10 |
The file pointer is set to the end of the file prior to each write. |
PR_TRUNCATE | 0x20 |
If the file exists, its length is truncated to 0. |
PR_SYNC | 0x40 |
If set, each write will wait for both the file data and file status to be physically updated. |
PR_EXCL | 0x80 |
With PR_CREATE_FILE, if the file does not exist, the file is created. If the file already exists, no action and NULL is returned. |
More
There are more methods and properties on nsIFile
and nsILocalFile
interfaces, please refer to documentation on XulPlanet. Those methods/properties are mostly self-explanatory, so we haven't included examples of using them here.