File IO: Difference between revisions

From MozillaZine Knowledge Base
Jump to navigationJump to search
(re-format)
Line 1: Line 1:
{{extdev}}
{{extdev}}


This article describes local file input/output in Javascript.
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 IO is available at [http://xulplanet.com/references/xpcomref/group_FilesandStreams.html XulPlanet.com].
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==
==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].
There are a few Javascript wrappers for I/O 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).
MonkeeSage's module is much smaller and very easy to use (simple examples are included in the module).


==Creating a file object ("opening" files)==
==Creating a file object ("opening" files)==
<pre>var file = Components.classes["@mozilla.org/file/local;1"].
<pre>
var file = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("/home");</pre>
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.
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 ''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.
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==
==Getting special files==
Line 24: Line 25:
get("ProfD", Components.interfaces.nsIFile);</pre>
get("ProfD", Components.interfaces.nsIFile);</pre>


Here are some strings you can put in place of "ProfD" (stolen from MonkeeSage's IO module comments)
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"
{| border="1" cellpadding="5" rules="all"
Line 56: Line 57:
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].
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==
==User input via nsIFilePicker==
See [[Dev : nsIFilePicker]].
See [[Dev : nsIFilePicker]].


==nsIFile and path strings==
==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]:
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
<pre>
// file is nsIFile
var ios = Components.classes["@mozilla.org/network/io-service;1"].
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
getService(Components.interfaces.nsIIOService);
var fileHandler = ios.getProtocolHandler("file").
var fileHandler = ios.getProtocolHandler("file").
QueryInterface(Components.interfaces.nsIFileProtocolHandler);
QueryInterface(Components.interfaces.nsIFileProtocolHandler);
var URL = fileHandler.getURLSpecFromFile(file);</pre>
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.
To load from ''file://'', ''http://'', ''chrome://'', ''resource://'' and other URLs directly, use <code>[http://xulplanet.com/references/xpcomref/ifaces/nsIChannel.html nsIChannel]</code>. Until a knowledge base article on that 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!
Note: do '''not''' use <code>nsILocalFile::persistentDescriptor</code> 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.
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==
==Storing nsILocalFile in preferences==
Line 111: Line 114:


===Get a file in given directory===
===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.
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.


Notes: avoid using <tt>dir.path+"\\"+"myfile.txt"</tt>, as it is not cross-platform code.
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>
Using something like  
is possible, but <code>nsIFile::append()</code> is much easier to read and is guaranteed to work on all platforms Mozilla itself works.
<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===
===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>.
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)
<pre>
// file is the given directory (nsIFile)
var entries = file.directoryEntries;
var entries = file.directoryEntries;
var array = [];
var array = [];
Line 129: Line 131:
   entry.QueryInterface(Components.interfaces.nsIFile);
   entry.QueryInterface(Components.interfaces.nsIFile);
   array.push(entry);
   array.push(entry);
}</pre>
}
</pre>


==Reading from a file==
==Reading from a file==


===Simple===
===Simple===
'''Note: this code may have to be changed - see [https://bugzilla.mozilla.org/show_bug.cgi?id=278786 bug 278786]'''
<pre>// |file| is nsIFile
<pre>// |file| is nsIFile
var data = "";
var data = "";
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
Line 190: Line 193:


==Writing to a file==
==Writing to a file==
<pre>// file is nsIFile, data is a string
<pre>
// file is nsIFile, data is a string
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
.createInstance(Components.interfaces.nsIFileOutputStream);


// use 0x02 | 0x10 to open file for appending.
// use 0x02 | 0x10 to open file for appending.
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
foStream.write(data, data.length);
foStream.write(data, data.length);
foStream.close();</pre>
foStream.close();
</pre>


Flags parameter to the <tt>nsIFileOutputStream::init()</tt> function
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]).
(For more information refer to [http://lxr.mozilla.org/seamonkey/source/nsprpub/pr/include/prio.h prio.h file]).


Line 232: Line 235:
|}
|}


[[Category:Development|File IO]] [[Category:Example code|File IO]]
[[Category:Development|File I/O]] [[Category:Example code|File I/O]]

Revision as of 16:12, 2 February 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 MonkeeSage's 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)

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].

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 nsIChannel. Until a knowledge base article on that is written, 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 (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.

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 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

Simple

Note: this code may have to be changed - see bug 278786

// |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); 
data += 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

// 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.