HTML5 FileSystem APIを使って上書き処理をする方法

HTML5 FileSystem APIを使うと、ユーザのローカルファイルシステムにファイルを書いたりすることができます。もちろん自由にファイルを読み込めるわけではなく、Chrome Webブラウザが持っているサンドボックス内に通常は限定されます(Chromeアプリの場合はその限りではないですが)。オフライン対応アプリケーションを開発する場合には、非常に魅力的なAPIです。

僕の場合はChromeアプリ内でFileSystem APIを使っていたのですが、「既存ファイルの上書き処理」をする際に困ったことと解決策を、本エントリで紹介してみたいと思います。FileSystem API自体の説明は、「 FileSystem API について知る」をご覧ください。

まず、普通にファイルに何か書き込みを行いたい場合、以下のようにコーディングすると思います。

function onInitFs(fs) {
  fs.root.getFile('hoge.txt', {create: true, exclusive: false}, function(fileEntry) {
    fileEntry.createWriter(function(fileWriter) {
      fileWriter.onerror = function(e) {
        // Do something...
      };
      fileWriter.onwriteend = function(e) {
        console.log('Write completed.');
      };
      var contents = "...";
      var blob = new Blob([contents], {type: "text/pain"});
      fileWriter.write(blob);
    }, errorHandler);
  }, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1*1024*1024, onInitFs, errorHandler);

上記のコードにおいて、もし既にhoge.txtファイルが存在していた場合、既存ファイルの内容と、書き込みたい内容(上記のコードのcontents変数値)次第で、ファイルの内容が壊れたものになります。具体的には、上書きはされるのですが、内容の大きさが「書き込みたい内容 < 既存ファイルの内容」だった場合、既存ファイルの内容が一部末尾に残ってしまいます。例をあげると、例えば既存ファイルの内容が、

“Hello, world!”

だった場合、contents = “Hi, world!” を上記コードで書き込んだ場合、

“Hi, world!ld!”

となってしまいます。

これを避けるためにいくつかの方法が考えられますが、最も手軽なコードとして、僕は以下に行き着きました。

function onInitFs(fs) {
  fs.root.getFile('hoge.txt', {create: true, exclusive: false}, function(fileEntry) {
    fileEntry.createWriter(function(fileWriter) {
      fileWriter.onerror = function(e) {
        // Do something...
      };
      fileWriter.onwriteend = function(e) {
        if (fileWriter.length === 0) {
          var contents = "...";
          var blob = new Blob([contents], {type: "text/pain"});
          fileWriter.write(blob);
        } else {
          console.log('Write completed.');
        }
      };
      fileWriter.truncate(0);
    }, errorHandler);
  }, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1*1024*1024, onInitFs, errorHandler);

まず、createWriter()関数に渡したコールバック関数で得られるfileWriterについて、その時点でのファイル内でのポジションはファイルの先頭です。ここで直ちにtruncate()関数に0を渡して、ファイルの長さを0にします。つまり、一旦ファイルの内容を消去するのです。この副作用として、onwriteendイベントが発火します。最初にこのイベントが発火したときのファイルの長さは0のはずなので、もしlengthが0だったときは書き込みたい内容をwrite()関数で書き込みます。書き込み終了後、再度onwriteendイベントが発火しますが、そのときにはlengthが0ではなくなってるはずなので、書き込み終了処理を行えば良い、ということです。

少なくともChromeアプリ内では上記のコードで正しくファイルの上書き処理(というか置き換え処理と言うべきか・・・)が行われているので、通常のWebページでも同じように動作すると思います。

database_panel_controller.js#L142

もし「ダメだったよ!」という場合は、僕に教えてください。。。

このエントリーをはてなブックマークに追加

関連記事

2023年のRemap

Remapにファームウェアビルド機能を追加しました

Google I/O 2023でのウェブ関連のトピック

2022年を振り返って

現在のRemapと今後のRemapについて