File I/O

この文書では、chrome JavaScript 内でローカルのファイル入出力を行う方法について解説しています。

ファイルシステムには、Mozilla XPCOM コンポーネント経由でアクセスできます。ローカル I/O のために利用されるコンポーネントのリストは XUL Planet にあります。

利用可能なライブラリ

いくつかの I/O に関する XPCOM コンポーネントの JavaScript ラッパが存在します。JSLibio.js (MonkeeSage が元) を参照してください。io.js モジュールはより小さくて、非常に簡単に利用できます。(簡単なサンプルがモジュール内にあります)

ファイルオブジェクトの作成 (ファイルを "開く")

var file = Components.classes["@mozilla.org/file/local;1"]
                     .createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("/home");

注意: initWithPath() へ渡すパスは、(<tt>"C:\\Windows"</tt> といった) ネイティブ形式である必要があります。もし、初期化に file:// URI を利用したいならば下を参照してください。

注意: initWithPath() / initWithFile() 関数はファイルが存在しなくても例外を投げません。例外は、isDirectory(), moveTo() といったファイルが存在する必要のあるメソッドを読んだ時に投げられます。

特殊ファイルを取得する

// get profile directory
var file = Components.classes["@mozilla.org/file/directory_service;1"]
                     .getService(Components.interfaces.nsIProperties)
                     .get("ProfD", Components.interfaces.nsIFile);

"ProfD" に入れるべき文字列は以下のものです。(MonkeeSage の I/O モジュールコンポーネントから抜粋)

文字列 意味
ProfD プロファイルディレクトリ
DefProfRt ユーザの (/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 (通常) インストールされたディレクトリ
Home OS ルートディレクトリ (/root など)
TmpD OS 一時ディレクトリ (/tmp など)
ProfLD Windows でのローカル設定; ネットワークキャッシュや fastload ファイルの保管場所
resource:app XULRunner アプリケーションでのアプリケーションディレクトリ

他の利用可能な文字列に関しては、ソースを見てください : xpcom/io/nsDirectoryServiceDefs.hxpcom/io/nsAppDirectoryServiceDefs.h

あなたの拡張のフォルダーを取得する

注意: Firefox/Thunderbird 1.5+ で動作します。1.0 では動きません。

拡張がインストールされているディレクトリを取得するには、nsIExtensionManager を次のように呼びます。

const id = "ID";
var ext = Components.classes["@mozilla.org/extensions/manager;1"]
                    .getService(Components.interfaces.nsIExtensionManager)
                    .getInstallLocation(id)
                    .getItemLocation(id); 
// ext は nsIFile のインスタンス、ext.path はディレクトリ文字列を保持します

ID を拡張の ID に置き換えてください。nsIFile に拡張のディレクトリが入って戻ります。この値は読み出し専用です。詳細については、ソースを見てください : toolkit/mozapps/extensions/public/nsIExtensionManager.idl

JavaScript XPCOM コンポーネント中では、ここ に述べられている特別な __LOCATION__ 変数を利用することもできます。 ここにあるように、この方法は、より信頼性が高く、Firefox/Thunderbird 1.5+ と 1.0 の両方で動作します。

フォルダーの作成

フォルダーを作成するには、nsIFile.create() を利用します。

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);
}

上のサンプルは、プロファイルフォルダー の中に "DIR" という名前のフォルダーを作成します。詳細は、nsIFile.create リファレンス を参照してください。

一時ファイルの作成

一時ファイルを作成するには、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);
// 作成したファイルに必要なことを行ってください
alert(file.path);

nsIFilePicker 経由のユーザからの入力

ファイル選択コンポーネント (nsIFilePicker) を、標準的なファイルを開く・保存するダイアログを開くのに利用できます。このコンポーネントは、nsIFile でユーザの指定したファイルを返します。

nsIFile とパス文字列

nsIFile.path をプラットフォーム依存のパス文字列を取得するのに利用可能です。たとえば、 <tt>"C:\Windows\System32"</tt> や <tt>"/usr/share"</tt> などです。

もし、ファイルの file:// URL や file:// URL から nsIFile を取得したいなら、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);

file://, http://, chrome://, resource:// や他の URL 形式から直接読み込むには、XMLHttpRequestnsIChannel を利用してください (サンプル)。

一般的には nsIFile::path を利用する必要はありません。可能ならば nsIFile を直接利用してください。以下のサンプルで、ユーザ設定にどのようにパスを保存するかを示しています。

nsILocalFile をオプションに保存する

次の二つのコード例は、ファイルパスをユーザのオプションに保存する正しい方法を示しています。(Mozilla でのオプションについての詳細):

絶対パス (nsILocalFile)

絶対パスをユーザのオプションに保存するには、次のコードを利用します。

// |file| is nsILocalFile
// 1. パスを 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. prefs からパスを読み込む
var file = prefs.getComplexValue("filename", Components.interfaces.nsILocalFile);

相対パス (nsIRelativeFilePref)

プロファイルフォルダーからの相対パスといった、上にリストされているフォルダーを親とする相対パスを保存するには、次のコードを利用します。

// 1. prefs へ書き込む
var relFile = Components.classes["@mozilla.org/pref-relativefile;1"]
                        .createInstance(Components.interfaces.nsIRelativeFilePref);
relFile.relativeToKey = "ProfD"; // ProfD もしくは上にリストされている文字列
relFile.file = file;             // |file| is nsILocalFile
prefs.setComplexValue("filename", 
     Components.interfaces.nsIRelativeFilePref, relFile);

// 2. prefs から読み込む
var value = prefs.getComplexValue("filename", 
     Components.interfaces.nsIRelativeFilePref);
// |value.file| is the file.

nsIFile でナビゲーションを行う

与えられたディレクトリのファイルを取得する

filensIFile で、なんらかのディレクトリをさしているとします。(ユーザプロファイルディレクトリなど) file.append("myfile.txt"); で、file が同じディレクトリの中の <tt>myfile.txt</tt> をさすようにできます。

: クロスプラットフォームでないことから、dir.path+"\\"+"myfile.txt" を利用しないでください。((path.search(/\\/) != -1) ? path + "\\" : path + "/") + "myfile.txt"; のようなものも可能ですが、nsIFile.append() がより簡単に読め、Mozilla が動作する全てのプラットフォームで動作が保障されています。

与えられたディレクトリのファイルを表示する

以下のコードは、与えられたディレクトリのサブディレクトリやサブファイルの nsIFile の配列を作成します。フォルダーかファイルかは、それぞれの entrynsIFile.isDirectory() と、nsIFile.isFile() を呼ぶことで判断できます。

// file は nsIFile のディレクトリです
var entries = file.directoryEntries;
var array = [];
while(entries.hasMoreElements())
{
  var entry = entries.getNext();
  entry.QueryInterface(Components.interfaces.nsIFile);
  array.push(entry);
}

ファイルを読み込む

: 以下のサンプルコードは non-ASCII 文字を含むテキストを処理できません。詳細とそれらの文字を含むテキストを読む方法については、Reading textual data を参照してください。

単純な方法

// |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, 0);
sstream.init(fstream); 

var str = sstream.read(4096);
while (str.length > 0) {
  data += str;
  str = sstream.read(4096);
}

sstream.close();
fstream.close();
alert(data);

行ごとに

// 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);

// 行を配列に読み込む
var line = {}, lines = [], hasmore;
do {
  hasmore = istream.readLine(line);
  lines.push(line.value); 
} while(hasmore);

istream.close();

// データで何かする
alert(lines);

非同期

以下のサンプルでファイルを UI スレッドとは独立に読み込めます。

// |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);

バイナリファイル

単純な例として、PNG ファイルのデータの取得方法

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 = Components.classes["@mozilla.org/file/local;1"]
                        .createInstance(Components.interfaces.nsILocalFile);
pngFile.initWithPath(url.path);

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());

ファイルに書き出す

': このコードは国際化されていません。non-ASCII文字列では、正常に動作しません。国際化については、Writing textual data を参照してください。

// file は nsIFile、data は文字列
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                         .createInstance(Components.interfaces.nsIFileOutputStream);

// ファイル追記の際は、0x02 | 0x10 を使う
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
foStream.write(data, data.length);
foStream.close();

nsIFileOutputStream::init() 関数のフラグパラメータは次のようなものです。 (詳細は nsprpub/pr/include/prio.h を参照してください)

フラグ: ファイルステータスフラグです。以下のBITフラグの bit ごとの OR を利用します。(最初の三つについては、どれか一つを利用します)

名前 説明
PR_RDONLY 0x01 読み込み専用
PR_WRONLY 0x02 書き出し専用
PR_RDWR 0x04 読み書き両方
PR_CREATE_FILE 0x08

ファイルが存在しないならば作成する。ファイルがあれば何もしない。

PR_APPEND 0x10

書き込みごとにファイルポインタはファイルの最後にセットされます。(追記モード)

PR_TRUNCATE 0x20

ファイルが存在すれば、長さを 0 にします。

PR_SYNC 0x40

書き込みごとにファイルデータとステータスが物理的にアップデートされるのを待ちます。

PR_EXCL 0x80

PR_CREATE_FILE と一緒に利用された場合、ファイルが存在しなければ作成されます。 ファイルが存在すれば、NULL を返してなにもしません。

バイナリファイルを書き込む

例として、PNG データをファイルに書き込むサンプルです。

// pngBinary は既に存在する
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, 600);
            
var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
                       .createInstance(Components.interfaces.nsIFileOutputStream);
stream.init(aFile, 0x04 | 0x08 | 0x20, 664, 0); // write, create, truncate
            
stream.write(pngBinary, pngBinary.length);
if (stream instanceof Components.interfaces.nsISafeOutputStream) {
    stream.finish();
} else {
    stream.close();
}

発展

nsIFilensILocalFile インターフェースにはよりたくさんのメソッドとプロパティーがありますので、 XUL Planet のドキュメントを参照してください。これらのメソッド・プロパティーは大半が一目瞭然ですので、ここにはサンプルを入れていません。

Document Tags and Contributors

Contributors to this page: Shimono, Masao, Mgjbot
最終更新者: Masao,