Code snippets:File I/O
From MDC
This article describes local file input/output in chrome JavaScript.
You access the file system using Mozilla XPCOM components. The list of components used for local I/O is available at XUL Planet.
[edit] 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).
[edit] Creating a file object ("opening" files)
var file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("/home");
NOTE: the path passed to initWithPath() should be in "native" form (e.g. "C:\\Windows"). If you need to use file:// URIs as initializers, see discussion of nsIFileProtocolHandler below.
NOTE: initWithPath() / initWithFile() functions don't throw an exception if the specified file does not exist. An exception is thrown when methods that require the file existence are called, e.g. isDirectory(), moveTo(), etc.
[edit] 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);
// NOTE: "file" is an object that implements nsIFile. If you want the
// file system path, use file.path
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) |
| ProfLD | Local Settings on windows; where the network cache and fastload files are stored |
| resource:app | application directory in a XULRunner app |
| DeskP | Windows desktop (e.g,. C:\Documents and Settings\username\Desktop) |
| Progs | User start menu programs directory (e.g., C:\Documents and Settings\username\Start Menu\Programs) |
Look in the Source for other strings available: xpcom/io/nsAppDirectoryServiceDefs.h, xpcom/io/nsDirectoryServiceDefs.h.
[edit] Getting your extension's folder
NOTE: This will only work in Firefox/Thunderbird 1.5+, not 1.0.
To get the directory that an extension is installed in, use an XPCOM component written in JavaScript. The special __LOCATION__ builtin will return an nsIFile of the component file:
extensiondir/component/GetExtensionDirectory.js:
var componentFile = __LOCATION__; var componentsDir = componentFile.parent; var extensionDir = componentsDir.parent;
[edit] 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, 0777);
}
The above example creates a folder called "DIR" in the Profile folder. For more information, refer to the nsIFile.create reference.
[edit] 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, 0666);
// do whatever you need to the created file
alert(file.path);
[edit] User input via nsIFilePicker
The file picker component (nsIFilePicker) can be used to open standard Open / Save dialogs. The components returns user-specified file as nsIFile.
[edit] 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.
[edit] 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):
[edit] 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);
[edit] 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.
[edit] Navigating with nsIFile
[edit] 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.
[edit] Enumerating files in given directory
The snippet below makes an array of nsIFiles 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);
}
[edit] Reading from a file
NOTE: The sample code below does not handle text with non-ASCII characters. See Reading textual data details on how to read text from other character sets.
[edit] 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, 0);
sstream.init(fstream);
var str = sstream.read(4096);
while (str.length > 0) {
data += str;
str = sstream.read(4096);
}
sstream.close();
fstream.close();
alert(data);
[edit] 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);
[edit] 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);
[edit] Binary File
For instance, to get the data in a PNG file:
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var url = ios.newURI(aFileURL, null, null);
if (!url || !url.schemeIs("file")) throw "Expected a file URL.";
var pngFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
pngFile.initWithPath(url.path);
var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
istream.init(pngFile, -1, -1, false);
var bstream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bstream.setInputStream(istream);
var bytes = bstream.readBytes(bstream.available());
[edit] 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, 0666, 0);
// write, create, truncate
// In a c file operation, we have no need to set file mode with or operation,
// directly using "r" or "w" usually.
foStream.write(data, data.length);
foStream.close();
Flags parameter to the nsIFileOutputStream::init() function
(For more information refer to nsprpub/pr/include/prio.h).
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. |
Note that the strings in the Name column of this table cannot be used in Javascript code. You have to use the octal constants.
[edit] Writing a Binary File
For example, here we can write PNG data to a file.
// pngBinary already exists
var aFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
aFile.initWithPath( "/tmp/mypicture.png" );
aFile.createUnique( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 600);
var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
stream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0); // write, create, truncate
stream.write(pngBinary, pngBinary.length);
if (stream instanceof Components.interfaces.nsISafeOutputStream) {
stream.finish();
} else {
stream.close();
}
[edit] Copy a stream to a file
This function returns a basic stream listener that copies all of the data from an input stream to a file. Takes an nsIFile as an argument, which is assumed to be open and created; depending on what you're doing, you might prefer to create the file in onStartRequest and close it in onStopRequest.
function StreamToFile(file)
{
var output = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
var buffer = Components.classes["@mozilla.org/network/buffered-output-stream;1"]
.createInstance(Components.interfaces.nsIBufferedOutputStream);
output.init(file, 0x02 | 0x08 | 0x20, 0664, null);
buffer.init(output, 8192);
return {
onStartRequest: function(request, context) { },
onDataAvailable: function onDataAvailable(request, context, stream, offset, count)
{
while(count > 0)
count -= buffer.writeFrom(stream, count);
},
onStopRequest: function(request, context) { }
};
}
[edit] More
There are more methods and properties on nsIFile and nsILocalFile interfaces, please refer to documentation on XUL Planet. Those methods/properties are mostly self-explanatory, so we haven't included examples of using them here.