FlashAirへのアップロード

最終更新: 2017/5

第5回では、FlashAirへファイルをアップロードするアプリを作成します。 upload.cgiを使用します。

事前準備

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

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

> cordova create cordovatutorial5 com.fixstars.flashair.tutorial CordovaTutorial5

また、プロジェクト作成後、 browserプラットフォーム及び以下のプラグインの追加を行ってください。

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

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

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

アプリのトップ画面作成

1. index.htmlを編集する

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

/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>
<p class="pos-center"><button type="button" onclick="location.href='tutorial4.html'">4.サムネイルの表示</button></p>
<p class="pos-center"><button type="button" onclick="location.href='tutorial5.html'">5.FlashAirへのアップロード</button></p>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/load.js"></script>
</body>
  • 39行目
    タップされたら tutorial5.htmlの画面へ遷移するボタンを追加しています。FlashAirへのアップロードは tutorial5.htmlの画面で行います。

2. 実行結果

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

Androidデバイスの場合

Androidデバイスのトップ画面

iOSデバイスの場合

iOSデバイスのトップ画面

FlashAirへのアップロード画面の作成

1. tutorial5.htmlを作成する

続いて、FlashAirへのアップロード画面を作成します。 tutorial4.htmlとの共通部分が多いので、コピーしたファイルを tutorial5.htmlとして保存して、9~24行目を以下のように変更してください。

/www/tutorial5.html

<title>Tutorial5</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">Upload</h1>
<div id="head-right"></div>
</div>
<div id="list"></div>
<hr>
<div id="upload">
<h3>Upload the file to the current directory</h3>
<button id="doUpload">Upload!</button>
</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/tutorial5.js"></script>
</body>
</html>
</html>
  • 9行目
    画面のタイトルを Tutorial5に変更しています。
  • 15行目
    ヘッダーのタイトルを Uploadに変更しています。
  • 20-23行目
    FlashAirへアプリ内に格納しているファイルをアップロードするボタン枠を追加しています。 アップロードするファイルについては後述します。
  • 27行目
    ロードするJavaScriptの内、 tutorial4.jstutorial5.jsへ変更しています。

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

次は tutorial5.jsの作成に移りますが、その前にアップロード処理時の確認用ダイアログを使えるようにするため、 Dialogsプラグイン を追加していきます。 追加コマンドは以下の通りです。

> cordova plugin add cordova-plugin-dialogs -save

3. tutorial5.jsを作成する

FlashAirへアップロード画面でロードする tutorial5.jsを作成していきます。 tutorial4.jsとの共通部分が多いので、コピーしたファイルを tutorial5.jsとして保存して、まず9~21行目を以下のように変更してください。

/www/js/tutorial5.js

function onDeviceReady(){
    getFileList("");
    $(document).on("click", "a.dir", function(){
        getFileList(this.text);
    });
    $(document).on("click", "a.file", function(){
        downloadFile(this.text, currentPath);
    });
    $("#doUpload").click(function(){
        navigator.notification.confirm("Are you sure you upload 'upload.jpg'?", confirmCallback, "UPLOAD", "YES,NO");
        function confirmCallback(buttonIndex) {
            switch (buttonIndex) {
                // Dialog closed without tapped any buttons.
                case 0:
                    break;
                // "YES" tapped
                case 1:
                    uploadFile();
                    break;
                // "NO" tapped
                case 2:
                    break;
            }
        }
    });
    document.addEventListener("backbutton", onBackKeyDown, false);
    $("#head-left").click(function(){
        onBackKeyDown();
    });
}
  • 17-18行目
    「Upload!」ボタンをタップしたときに、まず確認用のダイアログを表示させます。 確認ダイアログの表示にはDialogsプラグインの navigator.notification.confirm を使用します。 第1引数がメッセージ、第2引数がコールバック関数、第3引数がタイトル、第4引数がボタン名の配列になります。
  • 19-32行目
    コールバック関数の中身です。 今回の場合は「YES」ボタンをタップしたときのみ、 uploadFile関数が呼び出されるように設定しています。

続いて以下のアップロード処理について、最後の行(179行目)から追加してください。 尚、コードは 上級者向けチュートリアル - FlashAirへのアップロード を元にしていますが、inputタグを使ったファイルアップロードがAndroid 4.4(Kitkat)で使用できない為、マルチパートの構造を自作してPOSTする方法をとっています。

/www/js/tutorial5.js

// Upload the content.
function uploadFile(){
    var uppath = makePath(".");
    var cgi = "http://flashair/upload.cgi";
    var dt = new Date();
    // The range of years for FAT32 is from 1980 to 2107.
    var year = (dt.getFullYear() - 1980) << 9;
    var month = (dt.getMonth() + 1) << 5;
    var date = dt.getDate();
    var hours = dt.getHours() << 11;
    var minites = dt.getMinutes() << 5;
    var seconds = Math.floor(dt.getSeconds() / 2);
    var timestring = "0x" + (year + month + date).toString(16) + (hours + minites + seconds).toString(16);
    var param = cgi + "?WRITEPROTECT=ON&UPDIR=" + uppath + "&FTIME=" + timestring;
    // GET request upload.cgi parameters.
    $.get(param, function(){
        // Create boundary.
        var boundary = "--------------------FlashAir" + year.toString() + month.toString() + date.toString() + hours.toString() + minites.toString() + seconds.toString();
        var localReq = new XMLHttpRequest();
        var preReader = new FileReader();
        var sufReader = new FileReader();
        var localBuffer, faBuffer;
        // GET request upload file (as ArrayBuffer).
        localReq.open("GET", "img/upload.jpg", true);
        localReq.responseType = "arraybuffer";
        // POST request to FlashAir succeeds.
        var faReqSuccess = function(){
            navigator.notification.alert("Upload complete!", function(){
                // Update the content list.
                getFileList(".");
            }, "UPLOAD", "OK");
        };
        // Suffix loading succeeds.
        var sufReaderSuccess = function(){
            // Connect HTTP Body suffix.
            faBuffer = connectBuffer(faBuffer, sufReader.result);
            var faReq = new XMLHttpRequest();
            // Make a POST request to upload a file to FlashAir.
            faReq.open("POST", cgi, true);
            // Set HTTP Headers.
            faReq.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
            faReq.onload = faReqSuccess;
            faReq.send(faBuffer);
        };
        // Prefix loading succeeds.
        var preReaderSuccess = function(){
            // Connect HTTP Body prefix and upload file.
            faBuffer = connectBuffer(preReader.result, localBuffer);
            // HTTP Body suffix
            var suffix = "\r\n" + "--" + boundary + "--";
            sufReader.onload = sufReaderSuccess;
            // Load suffix (as ArrayBuffer).
            sufReader.readAsArrayBuffer(new Blob([suffix]));
        };
        // GET request upload file succeeds.
        var localReqSuccess = function(){
            localBuffer = localReq.response;
            // HTTP Body prefix
            var prefix = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"userfile\"; filename=\"upload.jpg\"\r\n" + "Content-Type: image/jpeg\r\n\r\n";
            preReader.onload = preReaderSuccess;
            // Load prefix (as ArrayBuffer).
            preReader.readAsArrayBuffer(new Blob([prefix]));
        };
        localReq.onload = localReqSuccess;
        localReq.send(null);
    });
    return false;
}
// Add "addBuffer" to "oriBuffer" (as ArrayBuffer).
function connectBuffer(oriBuffer, addBuffer){
    var uint8Array = new Uint8Array(oriBuffer.byteLength + addBuffer.byteLength);
    uint8Array.set(new Uint8Array(oriBuffer), 0);
    uint8Array.set(new Uint8Array(addBuffer), oriBuffer.byteLength);
    return uint8Array.buffer;
}
  • 181-194行目
    まずは upload.cgiのパラメータを確定させ、GETリクエストを行っています。 各パラメータについては upload.cgiをご確認ください。
  • 202-203,242-243行目
    アプリ内に置いてあるファイル( img/upload.jpg)をGETリクエストしています。 responseTypeには arraybufferを指定して、バイナリデータとして取り扱います。 リクエスト成功時のコールバック関数は localReqSuccessです。
  • 205-210行目
    faReqSuccess(220行目)のコールバック関数の中身です。 Dialogsプラグインの navigator.notification.alert を使用したアラートダイアログを表示します。 第1引数がメッセージ、第2引数がアラートダイアログを閉じたときに呼ばれるコールバック関数、第3引数がタイトル、第4引数がボタン名になります。 そしてコールバック関数の中で、 getFileList(".")を呼び出すことでコンテンツリストが更新され、アップロードしたファイルが確認できるようになります。
  • 212-222行目
    sufReaderSuccess(229行目)のコールバック関数の中身です。 upload.cgiを使用して、FlashAirへ「upload.jpg」をPOSTリクエストしています。 リクエスト成功時のコールバック関数は faReqSuccessです。
  • 224-232行目
    preReaderSuccess(238行目)のコールバック関数の中身です。 FileReader.readAsArrayBuffer()を利用して、HTTPボディ部のアップロードファイル本体の後につけるString( suffix)をArrayBufferとして読み出すようにしています。 読み出し成功時のコールバック関数は sufReaderSuccessです。
  • 234-241行目
    localReqSuccess(242行目)のコールバック関数の中身です。 FileReader.readAsArrayBuffer()を利用して、HTTPボディ部のアップロードファイル本体の前につけるString( prefix)をArrayBufferとして読み出すようにしています。 読み出し成功時のコールバック関数は preReaderSuccessです。
  • 248-253行目
    元のArrayBufferに追加したいArrayBufferを追加して返す関数です。

4. tutorial5.jsで指定している画像を追加する

tutorial5.jsで指定したアップロード用の画像を /www/img以下に保存します。

upload.jpg

upload.jpg

5. 実行結果

上記の編集が完了したら、ビルドを行い、アプリのインストールを行います。 アプリインストール後、FlashAirへ無線LAN接続していれば、トップ画面の「5.FlashAirへのアップロード」ボタンをタップしたときに、画面下部にファイルをアップロードするボタンの枠が表示されます。 そして、この中の「Upload!」ボタンをタップすると、表示しているカレントディレクトリに「upload.jpg」ファイルがアップロードされ、コンテンツリストの表示が更新されます。

Androidデバイスの場合

androidデバイスのルートディレクトリの表示画面

1.アップロードボタンをタップする前のコンテンツリスト

androidデバイスのアップロード確認ダイアログ

2.アップロードボタンタップ後の確認ダイアログ

androidデバイスのアップロード完了アラート

3.アップロード完了後のアラートダイアログ

androidデバイスのアップロード完了後の画面

4.アラートダイアログを閉じた後に更新して表示されるコンテンツリスト


iOSデバイスの場合

iOSデバイスのルートディレクトリの表示画面

1.アップロードボタンをタップする前のコンテンツリスト

iOSデバイスのアップロード確認ダイアログ

2.アップロードボタンタップ後の確認ダイアログ

iOSデバイスのアップロード完了アラート

3.アップロード完了後のアラートダイアログ

iOSデバイスのアップロード完了後の画面

4.アラートダイアログを閉じた後に更新して表示されるコンテンツリスト

サンプルコード

リポジトリを見る(GitHub)

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