るさんちまん

技術メモとか雑記とか

JavaScriptでローカルにファイルを保存する方法(その1)

JavaScriptでローカルにファイルを保存する方法を調べたので、メモ。
動作確認はMac OS X10.9.2、Google Chrome 33で行っています(他のブラウザについては、参考リンクにもある JavaScriptからローカルファイルを作成する方法まとめ - あらびき日記 をご覧ください)。

Chromeでローカルにファイルを保存するには、File API: Writerを使います。
単純なテキストファイルをローカルに保存するサンプルは下記のようになります。

サンプルコード

<script type="text/javascript">
function writeToLocal(filename, content) {
    // chrome以外は弾く
    var ua = navigator.userAgent.toLowerCase();
    if (ua.indexOf('chrome') == -1) {
        alert("This Page is Google Chrome only!");
    }

    function errorCallback(e) {
        alert("Error: " + e.name);
    }

    function fsCallback(fs) {
        fs.root.getFile(filename, {create: true}, function(fileEntry) {
            fileEntry.createWriter(function(fileWriter) {

                fileWriter.onwriteend = function(e) {
                    alert("Success! : " + fileEntry.fullPath);
                };

                fileWriter.onerror = function(e) {
                    alert("Failed: " + e);
                };

                var output = new Blob([content], {type: "text/plain"});
                fileWriter.write(output);
            }, errorCallback);
        }, errorCallback);
    }
    // クオータを要求する。PERSISTENTでなくTEMPORARYの場合は
    // 直接 webkitRequestFileSystem を呼んでよい
    webkitStorageInfo.requestQuota(PERSISTENT, 1024,
        webkitRequestFileSystem(PERSISTENT, 1024, fsCallback, errorCallback),
    errorCallback);
}
writeToLocal("hoge.txt", "foo\n");
</script>

サンプルコードの内容について、要点だけ説明します。

ファイルシステムの要求

まず、 webkitRequestFileSystem()によってファイルシステムを要求します。
第一引数にはwindow.PERSISTENT(永続データ)、もしくはwindow.TEMPORARY(一時データ)を指定します。
ここで注意なのがPERSISTENTを指定した場合で、データ保存許可をユーザから得なくてはいけません。
サンプルコードのwebkitStorageInfo.requestQuota()がそれで、ページ表示時に最上部に許可ダイアログが表示されます。ここで一度許可すると次回以降はrequestQuota()する必要はありません。
また、TEMPORARYを指定した場合にはrequestQuota()する必要はなく、単にwebkitRequestFileSystem()を呼べばよいです。

ファイル書き込み

fs.root.getFile()すると、内部的にファイルが作成されます。
最後に、Blobデータを作成し、fileWriter.write()でファイルに書き込みを行います。シンプルですね。
ただ、ここでもハマりポイントがあって、Blob作成にはBlob()を使いましょう。
他のページだとBlobBuilder()WebKitBlobBuilder()を使っているものが多いですが、BlobBuilder()は非推奨、WebKitBlobBuilder()Chromeから既に削除されています!

作成されたファイルは、/Users/USERNAME/Library/Application Support/Google/Chrome/Default/File System/
以下に保存されます。
PERSISTANTで保存した場合は、上記ディレクトリ/(数字3文字のディレクトリ)/p
TEMPORARYで保存した場合は、上記ディレクトリ/(数字3文字のディレクトリ)/t
以下に保存されます。
その中で、さらに(数字2文字のディレクトリ)/(数字列だけの拡張子なしファイル)
に保存されます。なので、実はwriteToLocal()の第一引数で指定したファイル名はほぼ無意味です。
ただ、同じファイル名を指定すると同じファイルに保存され、違うファイル名を指定すると、数字が1増えたファイル名で保存されるようです。

まとめ

JavaScript+Google Chromeでファイル保存する方法はあるものの、ファイルの保存先が自由に選べなかったり(そりゃそうだが)、ファイル名を指定できなかったりで、使い勝手が悪いです。
ファイル保存先を指定するリンクを作成して、そこからファイルを保存する方法があるので、そのうちそのエントリも書きます。