Monaca（Cordova）のCameraプラグインで取得した写真をBlob形式に変換する方法

写真を外部ストレージにアップロードする場合などに、Blob形式の写真データが必要になることがあるかと思います。しかしCameraプラグインの写真の取得形式はURIまたはｂａｓｅ６４になるため、そのままではBlobデータとして扱えません。
JavaScriptでBlobデータを作るには`new Blob()`を使いますが、このコンストラクタ引数にはArrayBuffer（など）の形式のデータを指定しなければなりません。そのため、写真のURIから最終的にBlobへ変換するには、少々面倒ですが以下の手順を経由する必要があります。

1. Cameraプラグインで写真のURIを取得
2. URIからFileEntryに変換
3. FileEntryからFileオブジェクトを取得し、ArrayBufferに変換
4. ArrayBufferからBlobに変換

変換処理には、HTML5のFile APIを利用します。
それでは各手順を詳しく解説していきます。

## 1. Cameraプラグインで写真のURIを取得
まずは写真をURI形式で取得します。

```javascript
// 写真を読み込み
navigator.camera.getPicture(onSuccess, onError, { 
  sourceType : Camera.PictureSourceType.PHOTOLIBRARY,
  destinationType: Camera.DestinationType.FILE_URI
});
      
// 写真のURIが取得される
function onSuccess(imageURI) {
  // URIからFileEntryに変換
  URItoFileEntry(imageURI);
}

// エラー時の処理
function onError(error) {
  console.log(error);
}
```

## 2. URIからFileEntryに変換
`FileEntry`は、ローカルのファイルシステム内のファイルを表すインターフェースです。
`window.resolveLocalFileSystemURL()` によって、`FileEntry`を取得することができます。
この処理は非同期的に実行され、処理が完了すると第二引数に指定したコールバック関数に`FileEntry`が渡されます。

```javascript
// URIからFileEntryに変換する
function URItoFileEntry(imageURI) {
  window.resolveLocalFileSystemURL(imageURI, fileEntryToArrayBuffer, onError);
}
```

## 3. FileEntryからFileオブジェクトを取得し、ArrayBufferに変換
`FileEntry.file()`によって、読み込み可能なファイルを表す`File`オブジェクトを取得します。
そしてファイルの読み込み機能を提供する`FileReader`オブジェクトを生成し、`FileReader.readAsArrayBuffer（）`によって、`File`オブジェクトを`ArrayBuffer`形式に変換します。このメソッドは非同期で実行され、読み込みが完了すると`FileReader.onloadend`イベントが発火します。

```javascript
// FileEntryからFileオブジェクトを取得し、さらにArrayBufferに変換する
function fileEntryToArrayBuffer(fileEntry) {
  // Fileオブジェクトを取得
  fileEntry.file((file) => { 
    // FileReaderを生成
    let reader = new FileReader();
    // ArrayBuffer形式への変換完了時の処理
    reader.onloadend = ArrayBufferToBlob;
    // ArrayBuffer形式に変換
    reader.readAsArrayBuffer(file);
  }, onError);
}

// エラー時の処理
function onError(error) {
  console.log(error);
}
```

## 4. ArrayBufferからBlobに変換
`FileReader.onloadend`イベントのイベント引数に、`ArrayBuffer`オブジェクトが格納されています。あとはこの`ArrayBuffer`オブジェクトを配列形式にして`new Blob()`のコンストラクタに指定するだけです。

```javascript
// ArrayBufferからBlobに変換する
function ArrayBufferToBlob(event) {
  let blob = new Blob([event.target.result])
}
```

## ソースコード全文
```javascript
// 写真を読み込み
navigator.camera.getPicture(onSuccess, onError, { 
  sourceType : Camera.PictureSourceType.PHOTOLIBRARY,
  destinationType: Camera.DestinationType.FILE_URI
});
      
// 写真のURIが取得される
function onSuccess(imageURI) {
  // URIからFileEntryに変換
  URItoFileEntry(imageURI)
}

// URIからFileEntryに変換する
function URItoFileEntry(imageURI) {
  window.resolveLocalFileSystemURL(imageURI, fileEntryToArrayBuffer, onError);
}
    
// FileEntryからFileオブジェクトを取得し、さらにArrayBufferに変換する
function fileEntryToArrayBuffer(fileEntry) {
  // Fileオブジェクトを取得
  fileEntry.file((file) => { 
    // FileReaderを生成
    let reader = new FileReader();
    // ArrayBuffer形式への変換完了時の処理
    reader.onloadend = ArrayBufferToBlob;
    // ArrayBuffer形式に変換
    reader.readAsArrayBuffer(file);
  }, onError);
}

// ArrayBufferからBlobに変換する
function ArrayBufferToBlob(event) {
  let blob = new Blob([event.target.result])
}

// エラー時の処理
function onError(error) {
  console.log(error);
}
```

このようにFile APIを使うと、バイナリデータを様々な形式に変換することができます。
CameraプラグインはFile APIとの併用によって、できることが増えると覚えておくと良いでしょう。