This article needs a technical review. How you can help.
Use of OS.File is preferred over the examples in this article. Only use these legacy interfaces if OS.File is not available to you.
This article describes local file input/output in chrome JavaScript.
You access the file system using Mozilla XPCOM components.
nsIFile
. For legacy reasons, there is also an nsILocalFile
interface.Creating an nsIFile
object
Components.utils.import("resource://gre/modules/FileUtils.jsm"); var file = new FileUtils.File("/home");
Or the same without using FileUtils.jsm:
var file = Components.classes["@mozilla.org/file/local;1"]. createInstance(Components.interfaces.nsILocalFile); file.initWithPath("/home");
Note: The path should be in the "native" form (for example"C:\\Windows"
). If you need to use file:// URIs as initializers, see discussion of nsIIOService
below.
Note: You can still get a file object even if the specified file does not exist, and no exception will be thrown. An exception is thrown only when methods that require the file to exist are called, e.g., isDirectory()
, moveTo()
, and so on.
Getting files in special directories
Components.utils.import("resource://gre/modules/FileUtils.jsm"); // get the "data.txt" file in the profile directory var file = FileUtils.getFile("ProfD", ["data.txt"]);
Or the same without using FileUtils.jsm:
// Get profile directory. var file = Components.classes["@mozilla.org/file/directory_service;1"]. getService(Components.interfaces.nsIProperties). get("ProfD", Components.interfaces.nsIFile); // Or using Services.jsm and Ci = Components.interfaces var file = Services.dirsvc.get("ProfD", Ci.nsIFile); // Append the file name. file.append("data.txt"); // Note: "file" is an object that implements nsIFile. If you want the // file system path, use file.path.
Here are some of the special locations the directory service supports:
(Scope: d = product-wide, f = profile wide)
String | Scope | Meaning |
---|---|---|
AChrom | d | % CurProcD%/chrome |
APlugns | d | % CurProcD%/plugins (Deprecated - use APlugnsDL) |
APlugnsDL | d | |
ComsD | n/a | % CurProcD%/components |
CurProcD | n/a | Current working directory (usually the application's installation directory). |
DefProfLRt | d | Location of user profile temporary directories live. |
DefProfRt | d | The root of the default profile directory (for example /root/.mozilla ). |
DefRt | d |
~/Desktop on Linux or Mac OS X, C:\Documents and Settings\username\Desktop on Windows). |
Desk | f | |
DfltDwnld | f | The default Downloads directory (for example, ~/Downloads on Mac OS X). |
Home | f | The user's home directory (for example, /home/username ). |
PrefD | f | Directory containing user prefs |
PrfDef | d | %installation%/defaults/pref |
ProfD | f | The profile directory. |
profDef | d | The profile defaults of the "current" locale. Should be the first choice. |
ProfDefNoLoc | d | %installation%/defaults/profile - The profile defaults of the "default" installed locale. Second choice when profDef is not available. |
ProfLD | f | Local Settings on Windows; where the network cache and fastload files are stored. |
Progs | d | User Start menu programs directory (for example, C:\Documents and Settings\username\Start Menu\Programs ). This is a Windows-specific value. |
TmpD | d | The operating system's temporary files directory (for example, /tmp on Mac OS X and Linux). |
UChrm | f | The user chrome directory in their profile: %profile%/chrome . |
resource:app | d | The application directory in an XULRunner application. |
Look in the source for other strings available:
xpcom/io/nsAppDirectoryServiceDefs.h
,xpcom/io/nsDirectoryServiceDefs.h
.- Other such keys as the "ProfD" key are available; check the known locations.
Enumerating drives on Windows
While you can use initWithPath("/")
on UNIX-like systems (Linux, Mac) to get the root of the file system, there's no such root on Windows. However, you can enumerate available drives using the following code:
Components.utils.import("resource://gre/modules/FileUtils.jsm"); var root = new FileUtils.File("\\\\."); var drivesEnum = root.directoryEntries, drives = []; while (drivesEnum.hasMoreElements()) { drives.push(drivesEnum.getNext(). QueryInterface(Components.interfaces.nsILocalFile).path); }
Creating Folders
FileUtils.getDir() can create a folder automatically if it doesn't exist yet:
Components.utils.import("resource://gre/modules/FileUtils.jsm"); var dir = FileUtils.getDir("ProfD", ["DIR"], true);
The above example creates a folder called "DIR"
in the user's profile folder. You can also create a directory explicitly; for more information refer to nsIFile.create()
.
Creating temporary files
To create a temporary file, use nsIFile.createUnique()
:
Components.utils.import("resource://gre/modules/FileUtils.jsm"); var file = FileUtils.getFile("TmpD", ["suggestedName.tmp"]); file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); // do whatever you need to the created file alert(file.path);
User input via nsIFilePicker
The file picker component, nsIFilePicker
, can be used to open standard Open / Save dialogs. It returns the user-specified file as nsIFile
.
nsIFile
and path strings
You can use nsIFile.path
to get a platform-specific path string, for example "C:\Windows\System32"
or "/usr/share"
.
To get a URL of a file, use nsIIOService.newFileURI()
:
// file is nsIFile Components.utils.import("resource://gre/modules/Services.jsm"); var url = Services.io.newFileURI(file); // url is a nsIURI; to get the string "file://...", see url.spec
To get an nsIFile
from the 'file://' URL, use nsIFileURL
:
// url is a nsIURI; see nsIIOService::newURI for getting a string into a nsIURI. var file = url.QueryInterface(Components.interfaces.nsIFileURL).file; // file is a nsIFile
To load from file://, https://, chrome://, resource:// and other URLs directly, use XMLHttpRequest or NetUtil.asyncFetch().
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 nsIFile 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 (nsIFile)
To store an arbitrary path in user preferences, use this code:
// |file| is nsILocalFile. Uses Services.jsm. // 1. Write path to prefs var prefs = Services.prefs.getBranch("extensions.myext."); prefs.setComplexValue("filename", Components.interfaces.nsIFile, 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.
Navigating with nsIFile
Get a file in given directory
Let us assume an nsIFile
file is pointing to some directory (for example a user profile directory). Then you may use file.append("myfile.txt");
to make it point to myfile.txt
inside that directory.
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.Enumerating files in given directory
The snippet below makes an array of nsIFile
s corresponding to sub-directories/"sub-files" of the 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
Read into a stream or a string
This will allow you to read a file without locking up the UI thread while reading. Please note that some I/O, such as getting file information and opening the file can still happen on the main thread and therefore block the UI for periods of time. This sample code uses NetUtil.jsm. So therefore for the first parameter, file, you can pass an nsIFile
object or a string (such as a jar path) See: NetUtil.asyncFetch:
Components.utils.import("resource://gre/modules/NetUtil.jsm"); NetUtil.asyncFetch(file, function(inputStream, status) { if (!Components.isSuccessCode(status)) { // Handle error! return; } // The file data is contained within inputStream. // You can read it into a string with var data = NetUtil.readInputStreamToString(inputStream, inputStream.available()); });
Read with content type hint
It's useful to provide a content type hint to prevent the file system from doing a potentially expensive content type look up (which would be synchronous I/O). In this case an nsIChannel
object has to be explicitly created from the file:
Components.utils.import("resource://gre/modules/NetUtil.jsm"); // Content type hint is useful on mobile platforms where the filesystem // would otherwise try to determine the content type. var channel = NetUtil.newChannel(file); channel.contentType = "application/json"; NetUtil.asyncFetch(channel, function(inputStream, status) { ... });
Writing to a file
Write a string
This sample uses NetUtil.jsm and FileUtils.jsm.
Components.utils.import("resource://gre/modules/NetUtil.jsm"); Components.utils.import("resource://gre/modules/FileUtils.jsm"); // file is nsIFile, data is a string // You can also optionally pass a flags parameter here. It defaults to // FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE; var ostream = FileUtils.openSafeFileOutputStream(file); var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. createInstance(Components.interfaces.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; var istream = converter.convertToInputStream(data); // The last argument (the callback) is optional. NetUtil.asyncCopy(istream, ostream, function(status) { if (!Components.isSuccessCode(status)) { // Handle error! return; } // Data has been written to the file. });
Copy a stream to a file
This function copies all of the data from an input stream to a file asyncronously. It overwrites the file if it is there, and if it is not there it creates a file then writes into it. It takes an nsIInputStream
and an nsIFile
as arguments, and uses NetUtil.jsm and FileUtils.jsm. The callback
argument is optional.
function StreamToFile(stream, file, callback) { var output = FileUtils.openSafeFileOutputStream(file) NetUtil.asyncCopy(stream, output, callback); }
Synchronous
Checking the existence of a file
Warning: Performing synchronous IO on the main thread can cause serious performance problems. As a result, using this method on the main thread is strongly discouraged!
// |file| is nsIFile if (file.exists()) { // ... }
Note: You should avoid checking the existence of a file, not only because of the synchronous I/O on the main thread this causes. There might be races between different processes and/or threads, e.g., a file could be immediately created or deleted after you check the existence but before you can perform any other actions such as opening the file for reading or writing.
Reading a file
Warning: Performing synchronous IO on the main thread can cause serious performance problems. As a result, using this method on the main thread is strongly discouraged!
// |file| is nsIFile var data = ""; var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. createInstance(Components.interfaces.nsIFileInputStream); var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. createInstance(Components.interfaces.nsIConverterInputStream); fstream.init(file, -1, 0, 0); cstream.init(fstream, "UTF-8", 0, 0); // you can use another encoding here if you wish let (str = {}) { let read = 0; do { read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value data += str.value; } while (read != 0); } cstream.close(); // this closes fstream alert(data);
Reading Line by Line
Warning: Performing synchronous IO on the main thread can cause serious performance problems. As a result, using this method on the main thread is strongly discouraged!
// 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);
Reading a Binary File
Warning: Performing synchronous IO on the main thread can cause serious performance problems. As a result, using this method on the main thread is strongly discouraged!
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 = url.QueryInterface(Components.interfaces.nsIFileURL).file; 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());
Writing a File
Warning: Performing synchronous IO on the main thread can cause serious performance problems. As a result, using this method on the main thread is strongly discouraged!
// 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. // if you are sure there will never ever be any non-ascii text in data you can // also call foStream.write(data, data.length) directly var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"]. createInstance(Components.interfaces.nsIConverterOutputStream); converter.init(foStream, "UTF-8", 0, 0); converter.writeString(data); converter.close(); // this closes foStream
nsIFileOutputStream.init()
function are documented in PR_Open. For more information refer directly to nsprpub/pr/include/prio.h
Writing a Binary File
Warning: Performing synchronous IO on the main thread can cause serious performance problems. As a result, using this method on the main thread is strongly discouraged!
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, 0600); var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Components.interfaces.nsIFileOutputStream); stream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0); // readwrite, create, truncate stream.write(pngBinary, pngBinary.length); if (stream instanceof Components.interfaces.nsISafeOutputStream) { stream.finish(); } else { stream.close(); }
More
There are more methods and properties on nsIFile
and nsILocalFile
interfaces; please refer to their documentation for more details. Those methods/properties are mostly self-explanatory, so we haven't included examples of using them here.