File IO: Difference between revisions

From MozillaZine Knowledge Base
Jump to navigationJump to search
m (changed URL)
 
(38 intermediate revisions by 10 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 Javascript.
 
You access the filesystem using Mozilla [http://xulplanet.com/tutorials/xultu/xpcom.html XPCOM] components. The list of components used for local IO is available at [http://xulplanet.com/references/xpcomref/group_FilesandStreams.html XulPlanet.com].
 
==Available libraries==
There are a few JavaScript wrappers for IO XPCOM components. See [http://jslib.mozdev.org/ JSLib] and MonkeeSage's [http://gratisdei.com/io.js IO module].
MonkeeSage's 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: the path passed to ''initWithPath()'' should be in 'native' form (eg. <tt>"C:\\Windows"</tt>). 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==
<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 "ProfD" (stolen from MonkeeSage's IO 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 an nsILocalFile from user using nsIFilePicker==
See [[Dev : nsIFilePicker]].
 
==nsIFile and path strings==
You can use nsIFile::path to get platform-specific path string, eg. <tt>C:\Windows\System32</tt> or <tt>/usr/share</tt>. If you want to get a ''file://'' URL of a file or an nsIFile 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 [http://xulplanet.com/references/xpcomref/ifaces/nsIChannel.html nsIChannel]. Until a knowledge base article on [[nsIChannel]] is written, [http://forums.mozillazine.org/viewtopic.php?p=921150#921150 here]'s an example for you.
 
Note: do '''not''' use nsILocalFile::persistentDescriptor to get the file path!
 
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 ([[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, ''file'' is an nsIFile pointing to some directory (f.e. a user profile directory). You can use this code: <tt>file.append("myfile.txt");</tt> - to make ''file'' point to myfile.txt inside that directory.
 
Notes: avoid using <tt>dir.path+"\\"+"myfile.txt"</tt>, as it is not cross-platform code.
Using something like
<tt>((path.search(/\\/) != -1) ? path + "\\" : path + "/") + "myfile.txt";</tt>
is possible, but the 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 <tt>|nsIFile|</tt>s corresponding to sub-files/subdirectories of given directory. You can tell files from folders by calling <tt>nsIFile::isDirectory()</tt> and <tt>nsIFile::isFile()</tt> methods on each <tt>|entry|</tt>.
 
<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==
 
===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 bytes = sstream.available();
data += sstream.read(bytes);
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==
<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.
stream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
stream.write(data, data.length);
stream.close();</pre>
 
Flags parameter to the <tt>nsIFileOutputStream::init()</tt> 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.
|}
 
[[Category:Development|File IO]] [[Category:Example code|File IO]]

Latest revision as of 16:01, 8 June 2011

Moved [1] (MDC).