File API: Writer, Directories and System

os0x(Shogo Ohta), 2010-12-16

Profile

Theme

File API

File APIとは

ローカルファイルを直接(サーバーサイドを介さず)(テキストやBlobとして)読み取ることができるAPI

という認識が多いかも?

それはFile APIの一部に過ぎません

正確には、(2010年12月時点で)File APIは3つの仕様に分かれています

3つのFile API

File API

File API: Writer

File API: Directories and System

というわけで、今日はFile WriterとFile Directories and System(以下、File System API)の紹介です

File Writerはファイルを作る・書きこむAPI

そのままですね。

File System APIってなんでしょう?

File API: Directories and System

This specification defines an API to navigate file system hierarchies, and defines a means by which a user agent may expose sandboxed sections of a user's local filesystem to web applications. It builds on [FILE-WRITER], which in turn built on [FILE-API], each adding a different kind of functionality.

この仕様はファイルシステム階層を操作するAPIを定義し、またユーザーエージェント(ウェブブラウザ)がウェブアプリケーション向けにユーザーのローカルファイルシステムを安全に解放する方法を定義します。

要するに

大雑把に言えば、ウェブアプリからハードディスク(に限らない記憶領域)にファイルを安全に保存・参照できるようにするための仕様です

File System APIで何が出来るのか?

例えばメールのように古いデータはほとんど使わないけど一応取っておきたい場合に、圧縮してローカルに保存しておくことができます

Picasa・iTunesのような画像・音声・動画の管理アプリケーションをブラウザ内で完結できます

File API(FileSystem API)はウェブアプリとデスクトップアプリの垣根を壊す決定的なAPI

実装編

データ型など、これまでのWebAPIにはなかった概念がいろいろ出てくるので、ざっくり整理していきます。

バイナリデータの扱いとTyped Array

File APIではバイナリデータを扱います。バイナリデータを従来のArrayで扱うには非効率なので、BlobとTyped Arrayが導入されました。

Typed ArrayはWebGLから派生して、File APIやWeb SocketにXMLHttpRequest Level 2などと関連した仕様として策定が進んでいます

Typed Array

いわゆる型付き配列の1種

符号無しの整数のUint8Array・Uint16Array・Uint32Array、符号付き整数のInt8Array・Int16Array・Int32Array、浮動小数点数のFloat32Array・Float64Arrayなどの種類があります

当然ながら普通の配列より高速にアクセスできて、しかもsliceすることで巨大なデータの一部分だけを効率的に処理することも可能になります

ただ、ECMAScript側でもバイナリデータを扱うためのAPIが提案されているので、このあたりの仕様は今後も大きな動きがあるかもしれません strawman:binary_data [ES Wiki]

Typed ArrayはWebGL由来なので、WebGLが有効な環境、つまりChrome(stable版の場合は about:flags での設定が必要)やFirefox4(about:configで設定)などで既に使うことができます

Typed Array の初期化

var uint_array = new Uint8Array(1024);
console.log(uint_array.buffer instanceof ArrayBuffer);

Uint8ArrayなどはArrayBufferViewインターフェースを継承しており、そのインスタンスはbufferプロパティを持っています

bufferプロパティはArrayBufferを継承したオブジェクトです

もちろん、自分でTyped Arrayを初期化することは多くないでしょう

XMLHttpRequestとArrayBuffer

var x = new XMLHttpRequest();
x.open('get','img/file.jpg');
x.responseType='arraybuffer';
// x.responseType='blob';
x.onload=function(r){
  console.log(x.response instanceof ArrayBuffer);
};
x.send();

XMLHttpRequest(Level 2のEditor's Draft)にresponseTypeが追加され、text、blob、documentを指定できます(2010年11月時点でWebKitはblobではなくarraybufferで実装している)

responseTypeにblobを指定すると、responseでBlobを取得できます

Blob

FileAPIで定義されているバイナリデータ向けインターフェース。Binary Large Object。

FileAPIではBlobはローカルファイルからFileReaderで読み取るだけだったが、File Writer APIではBlobBuilderを使って作ることができるようになりました(Chromeではプリフィックス付きのWebKitBlobBuilder)

var bb = new (window.BlobBuilder || window.WebKitBlobBuilder)();
bb.append('sample');
var blob = bb.getBlob();

appendできるのは、Blob、ArrayBuffer、文字列のどれか

var x = new XMLHttpRequest();
x.open('get','img/file.jpg');
x.responseType='arraybuffer';
x.onload=function(r){
  /* BlobBuilderでBlobに変換 今回はWebKit決め打ち */
  var bb = new WebKitBlobBuilder();
  bb.append(x.response);
  var blob = bb.getBlob();
  /* FileReaderでBlobの読み込み */
  var fr = new FileReader();
  /* 先頭256バイトをバイナリ文字列として */
  fr.readAsBinaryString(blob.webkitSlice(0, 256));
  fr.onload = function(){
    output.textContent = fr.result;
  };
};
x.send();

FileSystem API

まず、(window.)requestFileSystemで初期化(一時的なデータ(TEMPORARY)か永続的なデータ(PERSISTENT)かどうかと、容量を指定)します

初期化したFileSystemオブジェクトにはrootプロパティがあり、これを基点にgetFileメソッドでファイルを参照し、その中身を作ったり、中身を読み出したりすることができます

ファイルの中身を作ったり、読み出したりする部分は前述のFile API・File Writer APIに依存します。

webkitRequestFileSystem(PERSISTENT,
                  1024 * 1024,
                  function(fs) {
  /* コールバックでFileSystem
     オブジェクトを受け取る */
  /* getDirectoryにcreate: true
     オプションでフォルダを作る */
  fs.root.getDirectory("data", {
    create: true
    },
    function(dir){
      /* getFileにcreate: true
      オプションでファイルを作る */
      dir.getFile("tempfile.txt", {
          create: true
        },
        function (file) {
          console.log(fs);
          console.log(dir);
          console.log(file);
          temp_file = file;
        }
      );
    }
  );
});

temp_file.createWriter(function(writer) {
  console.log(writer);/* FileWriterオブジェクト*/
  writer.onwrite = function(e) {
    console.log('writer completed.');
  };
  writer.onerror = function(e) {
    console.log('write failed: ' + e);
  };
  var bb = new WebKitBlobBuilder();
  bb.append('HTML5!\n');
  bb.append('日本語');
  writer.write(bb.getBlob('text/plain'));
});

なお、Chromeでは、Windows7の場合

%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\FileSystem
に実際に保存されています。

FileSystem APIで保存されているファイルは、(window.)saveAsでローカルに保存し直す(いわゆるダウンロード)こともできます(WebKitでも未実装で、saveAsというメソッド名が短く衝突しやすいので変更される可能性が高い)

var fileSaver = window.saveAs(blob, "tempfile.txt");

createObjectURL(以前はcreateBlobURLという名前)でBlobをURLに変換することができ、そのURLをimg要素のsrcなどに設定することができます。

img.src = webkitURL.createObjectURL(blob);

FileSystem API楽しみですね!

Chromeの9以降のバージョンで、起動オプションに

 --unlimited-quota-for-files
 --allow-file-access-from-files

をつけることで実際に試すことができます。

もうすぐChrome拡張・Chrome Web AppsでFileSystem APIを活用できるようになります。