Cordova アプリ内でファイル操作を行える「cordova-plugin-file」

cordova-plugin-file というプラグインを使うと、Cordova で作ったアプリの中でファイルを生成したり編集したりすることができる。

今回もこのプラグインを使った iOS 向けのサンプルアプリを作ったので、実際にどんなコードになるかは、以下のリポジトリの feat/pluginFile ブランチを見てみてもらいたい。

プラグインのインストール

プラグインのインストールはいつもどおり cordova コマンドより。

$ cordova plugin add cordova-plugin-file

そういば、Cordova v7.0.0 以降は --save オプションを書かなくても config.xml に記述されるのがデフォルトになった。また、config.xmlpackage.json に同じ情報が同時に追記されるようになった。競合する設定が合った場合は package.json の記述が優先されるみたい。

ファイルはどこに格納されるのか?

プラグインによってファイルが操作できると言ったが、このファイルはどこに保持されるのか?答えは、プラグインの公式 README.md に記載されている。

iOS 実機の場合は、/var/mobile/Applications/【アプリ ID】/ 配下に決まったディレクトリがあり、その配下にファイルが置かれることになる。

iOS シミュレータの場合は、以下のようなパスに格納される。

/Users/【ユーザ名】/Library/Developer/CoreSimulator/Devices/【デバイス ID】/data/Containers/Data/Application/【アプリ ID】/

これらのフォルダにアクセスするための定数プロパティがプラグインによって用意されており、実装する際はこの定数プロパティを使えば良い。

例えば一時的なファイルを生成して扱うのであれば、アプリのディレクトリ直下から見て ./tmp/ ディレクトリ配下、つまり JS のコードでは cordova.file.tempDirectory というプロパティを使うと良い。また、同期したいファイルであれば、./Library/Cloud/ ディレクトリを指す cordova.file.syncedDataDirectory プロパティが良いだろう。

実際にファイル操作をしてみる

では実際にファイルを操作してみよう。JavaScript コードの書き方は、HTML5 で登場した FileSystem API と同じで、ディレクトリ指定部分に先程紹介したプロパティを使えば良い。

ファイルの存在チェック

ファイルが存在するか確認するコードであれば以下のようになる。

// ファイルのフルパスを作る
var fileFullPath = cordova.file.tempDirectory + 'Temp.txt';
window.resolveLocalFileSystemURL(fileFullPath, function(fileSystem) {
  if(fileSystem.isFile) {
    console.log('Temp.txt ファイルが存在する');
  }
  else {
    console.log('Temp.txt ファイルが存在しない');
  }
}, function(error) {
  console.log('ファイル存在確認中にエラーが発生', error.code);
});

error.code については、プラグインが以下のようにコード値と内容を決めている。例えば 1 なら「ファイルが存在しないよ」エラー、というワケだ。

ファイルを新たに作る・既存ファイルを取得する

ファイルを新たに作るのと、既存のファイルを取得して処理する基本構文は同じなので、まとめて紹介する。

// exclusive
//   true  : ファイルが存在する場合にファイルを新規生成しようとした時 (create: true) に、エラー扱いにしてエラー処理の関数を呼び出す
//   false : 上のような場合にもエラーにはせず、
//           ファイルが存在する場合にファイルを新規生成しようとした場合 (create: true) は既存ファイルの取得処理として扱う
//           ファイルが存在しない場合にファイルを取得しようとした時 (create: false) は、exclusive の値に関わらずエラーとなる
// create
//   true  : ファイルが存在しない場合にファイルを新規生成する
//           ファイルが存在する場合は、exclusive: true ならエラーとし、exclusive: false ならファイル取得処理 (create :false と同じ) として扱う
//   false : 既に存在するファイルを取得する
//           ファイルが存在しない場合は exclusive の値に関わらずエラーとなる
// 今回はファイルを新規作成するための処理にするため、create: true を設定する (exclusive: false なのでファイルが存在した場合もエラーにはしない)
var options = {
  exclusive: false,
  create: true
};

// 生成したい (or 取得したい) ファイル名
var fileName = 'Temp.txt';

// tmp/ ディレクトリ配下を操作する
window.resolveLocalFileSystemURL(cordova.file.tempDirectory, function(fileSystem) {
  console.log('tmp ディレクトリ配下を操作します');
  
  // ファイルを生成 (or 取得) する
  fileSystem.getFile(fileName, options, function(fileEntry) {
    // 生成 or 取得したファイル情報が FileEntry オブジェクトで返される
    console.log('ファイル生成 成功', fileEntry);
  }, function(getFileError) {
    console.log('ファイル生成 失敗', getFileError.code);
  });
}, function(error) {
  console.log('tmp ディレクトリ操作 エラー', error.code);
});

オプションとして渡す連想配列の createexclusive の Boolean 値を変えてやれば、これがそのまま既存ファイルの取得処理になるというワケ。

実はファイルの移動や削除、テキストファイルへの書き込みなどの場合も、この基本形は変わらずfileEntrymoveTo() したり remove() したりすることになるのである。

// create: false 指定で既存ファイルを取得したとして、そのファイルを削除するサンプル
fileEntry.remove(function() {
  console.log('ファイル削除 成功');
}, function(error) {
  console.log('ファイル削除 失敗', error.code);
});

テキストファイルの末尾に追記する

テキストファイルへの書き込みは fileEntry.createWriter() を使うのだが、ファイル末尾に追記していきたい場合は、少々手間がかかる。以下をまるごとスニペットにしてしまうのが良いだろう。

var fileName = 'Temp.txt';

window.resolveLocalFileSystemURL(cordova.file.tempDirectory, function(fileSystem) {
  fileSystem.getFile(fileName, { create: false }, function(fileEntry) {
    fileEntry.createWriter(function(writer) {
      // ファイルの末尾まで移動する
      writer.seek(writer.length);
      
      // 書き込み終了時の処理を定義する
      writer.onwriteend = function(event) {
        if(this.error) {
          console.log('ファイル追記 追記処理中にエラー発生', this.error, event);
        }
        else {
          console.log('ファイル追記 成功', event);
        }
      };
      
      // 書き込みたいテキストの用意 : ココでは現在日時 + 改行コードを書き込む
      var text = new Date() + '¥n';
      
      // テキスト書き込み
      writer.write(text);
    });
  }, function(getFileError) {
    console.log('ファイル操作 エラー', getFileError.code);
  });
}, function(error) {
  console.log('tmp ディレクトリ操作 エラー', error.code);
});

インデントが深くなっていくので、適宜関数化しておくと良い。

「ファイルの末尾に追記」を制御できると、テキストファイルにロギングしたりする関数も作れたりする。


以上。cordova-plugin-file プラグインはとりあえず入れておくとアプリ内のディレクトリ参照・ファイル操作が楽になるのでオススメ。