File IO: Difference between revisions

From MozillaZine Knowledge Base
Jump to navigationJump to search
m (changed URL)
 
(9 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{extdev}}
Moved [https://developer.mozilla.org/en/Code_snippets:File_I/O] (MDC).
 
[[Category:Redirects]]
This article describes local file input/output in chrome JavaScript.
 
You access the filesystem using Mozilla [http://xulplanet.com/tutorials/xultu/xpcom.html XPCOM] components. The list of components used for local I/O is available at [http://xulplanet.com/references/xpcomref/group_FilesandStreams.html XulPlanet.com].
 
==Available libraries==
There are a few JavaScript wrappers for I/O XPCOM components. See [http://jslib.mozdev.org/ JSLib] and [[io.js]] (original by [http://gratisdei.com/io.js 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)==
<pre>
var file = Components.classes["@mozilla.org/file/local;1"]
                    .createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("/home");
</pre>
 
Note, that the path passed to <code>initWithPath()</code> should be in &quot;native&quot; form (e.g. <tt>"C:\\Windows"</tt>). If you need to use file:// URIs as initializers, see below.
 
Also note, that <code>initWithPath()</code> / <code>initWithFile()</code> 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. <code>isDirectory()</code>, <code>moveTo()</code> etc.
 
==Getting special files==
<pre>
// get profile directory
var file = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
</pre>
 
Here are some strings you can put in place of <code>"ProfD"</code> (stolen from MonkeeSage's I/O module comments):
 
{| border="1" cellpadding="5" rules="all"
! 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: [http://lxr.mozilla.org/seamonkey/source/xpcom/io/nsDirectoryServiceDefs.h] [http://lxr.mozilla.org/seamonkey/source/xpcom/io/nsAppDirectoryServiceDefs.h].
 
===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 <code>nsIExtensionManager</code> like this:
const id = "ID";
var ext = Components.classes["@mozilla.org/extensions/manager;1"]
                    .getService(Components.interfaces.nsIExtensionManager)
                    .getInstallLocation(id)
                    .getItemLocation(id);
 
Replace ''ID'' with the extension's [http://developer.mozilla.org/en/docs/Install_Manifests#id ID], and this will return an <code>nsIFile</code> of the directory of the extension .  This value is read only.  For additional information, view the source [http://lxr.mozilla.org/mozilla1.8/source/toolkit/mozapps/extensions/public/nsIExtensionManager.idl#71].
 
==Creating Folders==
To create a folder, use <code>nsIFile.create()</code>:
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 to the [http://www.xulplanet.com/references/xpcomref/ifaces/nsIFile.html#method_create nsIFile Reference] at XULPlanet.
 
==Creating temporary files==
To create a temporary file, use <code>nsIFile.createUnique()</code>:
<pre>
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);
</pre>
 
==User input via nsIFilePicker==
See [[Dev : nsIFilePicker]].
 
==nsIFile and path strings==
You can use <code>nsIFile.path</code> to get platform-specific path string, e.g. <tt>"C:\Windows\System32"</tt> or <tt>"/usr/share"</tt>.
 
If you want to get a ''file://'' URL of a file or an <code>nsIFile</code> from ''file://'' URL, you need to use [http://xulplanet.com/references/xpcomref/ifaces/nsIFileProtocolHandler.html nsIFileProtocolHandler]:
<pre>
// 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);
</pre>
 
To load from ''file://'', ''http://'', ''chrome://'', ''resource://'' and other URLs directly, use [[XMLHttpRequest]] or [http://xulplanet.com/references/xpcomref/ifaces/nsIChannel.html nsIChannel] ([http://forums.mozillazine.org/viewtopic.php?p=921150#921150 example]).
 
Also note that generally you don't need to use <code>nsIFile::path</code>. Use <code>nsIFile</code> 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 ([[Dev : Using preferences|more about preferences in Mozilla]]):
 
===Absolute path (nsILocalFile)===
To store arbitrary path in user preferences, use this code.
<pre>
// |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);
</pre>
 
===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:
<pre>
// 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.
</pre>
 
==Navigating with nsIFile==
 
===Get a file in given directory===
Assume, <code>file</code> is an <code>nsIFile</code> pointing to some directory (f.e. a user profile directory). You can use <code>file.append("myfile.txt");</code> to make <code>file</code> point to <tt>myfile.txt</tt> inside that directory.
 
Note: avoid using <code>dir.path+"\\"+"myfile.txt"</code>, as it is not cross-platform at all. Using something like <code>((path.search(/\\/) != -1) ? path + "\\" : path + "/") + "myfile.txt";</code>
is possible, but <code>nsIFile::append()</code> 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 <code>|nsIFile|</code>s corresponding to sub-directories/&quot;sub-files&quot; of given directory. You can tell files from folders by calling <code>nsIFile::isDirectory()</code> and <code>nsIFile::isFile()</code> methods on each <code>entry</code>.
 
<pre>
// 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);
}
</pre>
 
==Reading from a file==
 
''Note'': This code is not intl-aware. Reading non-ASCII characters '''will not work correctly'''. See [http://developer.mozilla.org/en/docs/Reading_textual_data Reading textual data] for intl-aware code.
 
===Simple===
<pre>// |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);</pre>
 
===Line by line===
<pre>
// 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);
</pre>
 
===Asynchronously===
 
This will allow you to read a file without locking up the UI thread.
 
<pre>// |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);
</pre>
 
==Writing to a file==
''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>
// 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();
</pre>
 
Flags parameter to the <code>nsIFileOutputStream::init()</code> function
(For more information refer to [http://lxr.mozilla.org/seamonkey/source/nsprpub/pr/include/prio.h 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):
{| border="1" cellpadding="5" rules="all"
! 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 <code>nsIFile</code> and <code>nsILocalFile</code> interfaces, please refer to documentation on [http://xulplanet.com/references/xpcomref/group_FilesandStreams.html XulPlanet]. Those methods/properties are mostly self-explanatory, so we haven't included examples of using them here.
 
[[Category:Example code]] [[Category:XPCOM example code]] [[Category:JavaScript example code]]

Latest revision as of 16:01, 8 June 2011

Moved [1] (MDC).