File I/O

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.

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).

From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other types.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

This article describes local file input/output in chrome JavaScript.

You access the file system using Mozilla XPCOM components.

Note: File objects are 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

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

%CurProcD%/defaults - The root directory of all defaults directories.

The user's desktop directory (for example ~/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:

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://, http://, 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.

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.

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.

Enumerating files in given directory

The snippet below makes an array of nsIFiles 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!

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.
// 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
Note: The file status flags used in the 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.