コンテンツのダウンロード

第3回では、FlashAirのコンテンツをダウンロードするアプリを作成します。 ダウンロード処理では、CordovaのFileプラグイン及びFileTransferプラグインを使用します。


事前準備

1. プロジェクトを作成する

まずは作成するアプリのプロジェクトを作成します。 前回までと同じく、プロジェクトを作成したいディレクトリへ移動し、以下のコマンドを実行します。

> cordova create cordovatutorial3 com.fixstars.flashair.tutorial CordovaTutorial3

また、プロジェクト作成後、browserプラットフォーム及び第2回で使用したStatusbarプラグインconfig.xmlへのコード追加作業含む)の追加を行ってください。

2. 第2回で使用したファイルを追加する

第2回で使用したファイルを以下のようにコピーして追加します。

cordovatutorial3  
├─ hooks
├─ platforms
├─ plugins
├─ www
│   ├─ css
│   │   └─ load.css(上書きコピー)
│   ├─ img
│   │   └─ logo.png
│   ├─ js
│   │   ├─ jquery-3.1.1.min.js(上書きコピー)
│   │   ├─ load.js(上書きコピー)
│   │   └─ tutorial2.js(上書きコピー)
│   ├─ index.html(上書きコピー)
│   └─ tutorial2.html(上書きコピー)
└─ config.xml

アプリのトップ画面作成

1. index.htmlを編集する

アプリのトップ画面であるindex.htmlを編集します。 第2回の36~39行目を、以下のように変更してください。

/www/index.html
<p class="pos-center"><button type="button" onclick="location.href='tutorial2.html'">2.コンテンツリストの取得</button></p>
<p class="pos-center"><button type="button" onclick="location.href='tutorial3.html'">3.コンテンツのダウンロード</button></p>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/load.js"></script>
</body>
  • 37行目
    タップされたらtutorial3.htmlの画面へ遷移するボタンを追加しています。コンテンツのダウンロードはtutorial3.htmlの画面で行います。

2. 実行結果

上記の編集が完了したら、ビルドを行い、出来上がったアプリのインストールを行ってください。第2回と比べて、「3.コンテンツのダウンロード」ボタンを追加しています。

Androidデバイスの場合
Androidデバイスのトップ画面
iOSデバイスの場合
iOSデバイスのトップ画面

コンテンツのダウンロード画面の作成

1. tutorial3.htmlを作成する

続いて、コンテンツのダウンロード画面を作成します。tutorial2.htmlとの共通部分が多いので、コピーしたファイルをtutorial3.htmlとして保存して、9~24行目を以下のように変更してください。

/www/tutorial3.html
<title>Tutorial3</title>
<link rel="stylesheet" type="text/css" href="css/load.css">
</head>
<body onload="onLoad()">
<div id="header" class="pos-center">
<div id="head-left">&lt;<br>Back</div>
<h1 id="head-center">Download</h1>
<div id="head-right"></div>
</div>
<div id="list"></div>
<hr>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="js/tutorial3.js"></script>
</body>
</html>
  • 9行目
    画面のタイトルをTutorial3に変更しています。
  • 15行目
    ヘッダーのタイトルをDownloadに変更しています。
  • 18-19行目
    tutorial3.jsで取得するコンテンツリストをここに表示させます。
  • 22行目
    ロードするJavaScriptの内、tutorial2.jstutorial3.jsへ変更しています。tutorial3.jsについては後述します。

2. tutorial3.jsで使用するプラグインを追加する

次はtutorial3.jsの作成に移りますが、その前にそちらで使用するプラグインを追加していきます。 まず、ダウンロード処理で使用するFileTransferプラグインを追加します。 また、ダウンロード時に端末内のファイルへアクセス(書き込み)を行うのに必要なFileプラグインも追加します。 また、ファイルのダウンロード後、コンテンツの表示にウェブブラウザビューを使用するので、Inappbrowserプラグインも追加します。 それぞれの追加コマンドは以下の通りです。

> cordova plugin add cordova-plugin-file -save
cordova plugin add cordova-plugin-file-transfer -save
cordova plugin add cordova-plugin-inappbrowser -save

3. tutorial3.jsを作成する

コンテンツのダウンロード画面でロードするtutorial3.jsを作成していきます。 ダウンロード処理はFileTransferプラグインのdownloadを利用して行います。 また、tutorial2.jsと異なり、コンテンツリストは階層表示ができるようにします。

/www/js/tutorial3.js
var currentPath = "/";
var wlansd = new Array();
var doesFileExist = false;

function onLoad(){
  document.addEventListener("deviceready", onDeviceReady, false);
}

function onDeviceReady(){
  getFileList("");
  $(document).on("click", "a.dir", function(){
    getFileList(this.text);
  });
  $(document).on("click", "a.file", function(){
    downloadFile(this.text, currentPath);
  });
  document.addEventListener("backbutton", onBackKeyDown, false);
  $("#head-left").click(function(){
    onBackKeyDown();
  });
}

function onBackKeyDown(){
  if(doesFileExist == false){
    location.href = "index.html";
  } else if(currentPath == "/"){
    location.href = "index.html";
  } else {
    getFileList("..");
  }
}
// Get the content list.
function getFileList(dir){
  doesFileExist = false;
  var nextPath = makePath(dir);
  var url = "http://flashair/command.cgi?op=100&DIR=" + nextPath;

  $.get(url, function(data){
    //Save the current path.
    currentPath = nextPath;
    // Split lines by new line characters.
    wlansd = data.split(/\n/g);
    // Ignore the first line (title) and last line (blank).
    wlansd.shift();
    wlansd.pop();
    splitFileList(currentPath);
    wlansd.sort(cmptime);
    showFileList(currentPath);
    // Success to get content list.
    doesFileExist = true;
  });
}
// Make a Path to show next.
function makePath(dir){
  var arrPath = currentPath.split("/");

  if(currentPath == "/"){
    arrPath.pop();
  }
  if(dir == ".."){
    // Go to parent directory. Remove last fragment.
    arrPath.pop();
  } else if(dir != "" && dir != "."){
    // Go to child directory. Append dir to the current path.
    arrPath.push(dir);
  }
  if (arrPath.length == 1){
    arrPath.push("");
  }
  return arrPath.join("/");
}
// Split the content list data.
function splitFileList(){
  for(var i=0; i<wlansd.length; i++){
    var elements = wlansd[i].split(",");
    wlansd[i] = new Array();
    wlansd[i]["r_uri"] = elements[0];
    wlansd[i]["fname"] = elements[1];
    wlansd[i]["fsize"] = Number(elements[2]);
    wlansd[i]["attr"]  = Number(elements[3]);
    wlansd[i]["fdate"] = Number(elements[4]);
    wlansd[i]["ftime"] = Number(elements[5]);
  }
}
// Sort contents by date and time.
function cmptime(a,b){
  if(a["fdate"] == b["fdate"]){
    return a["ftime"] - b["ftime"];
  } else {
    return a["fdate"] - b["fdate"];
  }
}
// Show the content list.
function showFileList(){
  $("#list").html("");
  $.each(wlansd, function(){
    var file = this;
    var filelink = $("<a href='javascript:void(0)'></a>");
    var caption = file["fname"];
    var faDir = file["r_uri"];
    var fileobj = $("<div></div>");
    // Skip hidden file.
    if(file["attr"] & 0x02){
      return;
    }
    // Make a link to directories and files.
    if(file["attr"] & 0x10){
      filelink.addClass("dir");
    } else {
      filelink.addClass("file");
    }
    $("#list").append(
      fileobj.append(
        filelink.append(
          caption
        )
      )
    );
  });
}
// Download the content.
function downloadFile(fname, dir){
  var fileTransfer = new FileTransfer();
  var dlPath = dir !="/" ? dir + "/" + fname : dir + fname;
  var dlUrl = encodeURI("http://flashair" + dlPath);
  var destUrl = encodeURI("cdvfile://localhost/persistent/download/" + fname);

  fileTransfer.download(dlUrl, destUrl, function(entry) {
      window.open = cordova.InAppBrowser.open(entry.toURL(), "_blank", "location=no");
  }, function(error) {
    switch (error.code){
      case 1:
        alert("Failed to download the file. Error code is 'FILE_NOT_FOUND_ERR'");
        break;
      case 2:
        alert("Failed to download the file. Error code is 'INVALID_URL_ERR'");
        break;
      case 3:
        alert("Failed to download the file. Error code is 'CONNECTION_ERR'");
        break;
      case 4:
        alert("Failed to download the file. Error code is 'ABORT_ERR'");
        break;
      case 5:
        alert("Failed to download the file. Error code is 'NOT_MODIFIED_ERR'");
        break;
    }
  });
}
  • 1行目
    コンテンツリストの取得や表示で使用する(FlashAir上の)カレントディレクトリの変数を宣言しています。初期値は"/"(root)です。
  • 9-21行目
    devicereadyイベントが発火した後、最初にgetFileList('')でrootのコンテンツリストを取得・表示させています。 そして、class="dir"を持つリンクがタップされた場合はそのディレクトリのコンテンツリストを新たに表示させます。 また、class="file"を持つリンクがタップされた場合はそのファイルコンテンツをダウンロードするdownloadFile関数を呼び出します。
  • 23-31行目
    getFileList関数でコンテンツリストのデータを取得できなかった場合、もしくはカレントディレクトリがrootの場合は、トップ画面へ戻るようにしています。また、カレントディレクトリがrootでない場合は、一つ前(親)のディレクトリのコンテンツリストを表示するようにしています。
  • 33-52行目
    コンテンツリストのデータを取得する関数です。 36行目でcommand.cgi?op=100のコマンドを発行しています。FlashAirから返ってきたデータに対する処理は、2番目の引数であるコールバック関数(function(data) {...})の内部に記述します。 これは、CGIコマンドの実行が非同期的に行われるためです。取得したデータは48行目のshowFileList関数で表示させています。
  • 54-71行目
    FlashAir上での絶対パスを作成するヘルパー関数です。
  • 73-84行目
    取得したコンテンツリストのデータを細分化して格納する関数です。 細分化したデータは、r_uriがコンテンツのあるディレクトリ、fnameがコンテンツ名、fsizeがコンテンツのサイズ、attrがコンテンツの属性、fdateがコンテンツの作成日、ftimeがコンテンツの作成時間になります。
  • 86-92行目
    取得したコンテンツリストのデータを更新日時順に並べ替える関数です。
  • 94-120行目
    取得したコンテンツリストをHTML中に用意したlistの位置に表示させる関数です。
  • 123行目
    FileTransferのインスタンス変数を宣言しています。
  • 124-125行目
    ファイルのダウンロード元となるサーバのURLを設定します。
  • 126行目
    ダウンロードしたファイルの保存先を設定します。 これは端末上のファイルのフルパスで指定する必要があります。ただし、FileTransferプラグインのdownloadではcdvfile形式のパスを使用でき、これを使用すれば、プラットフォームに依存せず指定された場所(アプリ内)に保存されるようになります。cdvfileの詳細については公式のFileプラグインのcdvfile protocol及びFileTransferプラグインのBackwards ompatibility Notesをご覧ください。
  • 128-148行目
    FileTransferプラグインのdownloadを利用して、ファイルのダウンロードを行っています。第1引数はダウンロード元URL、第2引数はダウンロード先のURL、第3引数はダウンロードのサクセスコールバック関数、第4引数はダウンロードのエラーコールバック関数です。サクセスコールバック関数(function(entry) {...})では、Inappbrowserプラグインの機能を利用して、ダウンロードしたファイルコンテンツをウェブブラウザビューで表示します。また、エラーコールバック関数(function(error) {...})では、デバッグ用として受け取ったエラーコードのアラートを出力します。 (※本サンプルコードではFileTransferプラグインのエラーコードを表示していますが、実際のアプリでは適切なメッセージを表示するのがよいでしょう。)

4. 実行結果

プロジェクト内の全てのファイルの配置が完了したら、ビルドを行い、アプリへインストールします。 ここで一つ注意点があります。 Androidデバイスでは以下の画面の通り、インストール時にストレージへのアクセス権限の許可が必要になります。 これはFileプラグイン及びFileTransferプラグイン利用でandroid.permission.WRITE_EXTERNAL_STORAGEパーミッションの利用を設定するためです。 (※iOSデバイスでは許可が必要になる権限はありません。)

androidデバイスのストレージアクセス権限の許可画面

アプリインストール後、FlashAirへ無線LAN接続していれば、トップ画面の「3.コンテンツのダウンロード」ボタンをタップすることで、コンテンツリストが表示されます。 またリストの内、ファイルコンテンツをタップすれば、アプリ内へのダウンロードを行い、ウェブブラウザビューで表示されます。 (以下の右側の画面は「テスト.txt」ファイルをタップして、ダウンロード完了後に表示される画面です。)

Androidデバイスの場合
androidデバイスのコンテンツリストの表示画面
androidデバイスのファイルタップ後の画面
iOSデバイスの場合
iOSデバイスのコンテンツリストの取得画面
iOSデバイスのタップ後の画面

サンプルコード

リポジトリを見る(GitHub)

  • このサンプルコードはApache License, Version 2.0で提供されています。
  • 「cordova_tutorial_03」ディレクトリは、zipファイルで圧縮してPhoneGap Buildへアップロードすることでビルドできます。 ビルド方法はHelloWorldアプリをビルドするをご確認ください。
  • Cordovaプロジェクトで確認する場合は、「cordova_tutorial_03」ディレクトリ内のデータをプロジェクト内へ上書きコピーしてください。