File IO: Difference between revisions
m (→Reading from a file: link to devmo) |
m (→Writing to a file: link to devmo) |
||
Line 214: | Line 214: | ||
==Writing to a file== | ==Writing to a file== | ||
''Note'': This code is not intl-aware. Writing non-ASCII characters '''will not work correctly'''. | ''Note'': This code is not intl-aware. Writing non-ASCII characters '''will not work correctly'''. See [http://developer.mozilla.org/en/docs/Writing_textual_data Writing textual data] for intl-aware code. | ||
<pre> | <pre> |
Revision as of 13:37, 14 November 2005
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].
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.