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

最終更新: 2017/5

第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 Compatibility 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」ディレクトリ内のデータをプロジェクト内へ上書きコピーしてください。