もっと使おうPhoneGap/Cordova 2.0.0

第8回 File APIを使ったiOS/Androidアプリケーション作成[その5]

この記事を読むのに必要な時間:およそ 11 分

ファイルのダウンロード・アップロード(iOS/Android)

ファイルのダウンロード・アップロードを行うアプリケーションを作成します。要件は次のとおりです。

  • ダウンロードするファイルは,gihyo.jpのロゴ。PERSISTENTファイルシステムに保存
  • PERSISTENTファイルシステムからファイルを選択し,アップロード
  • アップロード先には,ファイルを受信してカレントディレクトリに保存するプログラムを指定する。PHPで実装

リスト2 ソースコード: File API Test (6)

  1: <!DOCTYPE html>
  2: <html>
  3:   <head>
  4:     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  5:     <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" />
  6:     <title>File API Test (6)</title>
  7:   </head>
  8:   <body>
  9:     <div class="app">
 10:       <h1>File API Test</h1>
 11:       <p>
 12:         <input id="download" type="button" value="download gihyo logo">
 13:       </p>
 14:       <ul id="fileList"></ul>
 15:     </div>
 16:     <script type="text/javascript" src="cordova-2.0.0.js"></script>
 17:     <script type="text/javascript">
 18:     var directoryEntry;
 19: 
 20:     document.addEventListener('deviceready', init, false);
 21:     function init() {
 22:       window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, getFilesFromDirectory, fail);
 23:       document.getElementById('download').addEventListener('click', downloadFile, false);
 24:     }
 25: 
 26:     function getFilesFromDirectory(fileSystem) {
 27:       directoryEntry = fileSystem.root;
 28:       var directoryReader = directoryEntry.createReader();
 29:       directoryReader.readEntries(putFileName, fail);
 30:     }
 31: 
 32:     function putFileName(entries) {
 33:       for ( index = 0; index < entries.length; index++ ) {
 34:         if ( entries[index].isFile ) {
 35:           var listItem = document.createElement('li');
 36:           listItem.textContent = entries[index].name;
 37:           listItem.title = entries[index].fullPath;
 38:           listItem.addEventListener('click', uploadFile, false);
 39:           document.getElementById('fileList').appendChild(listItem);
 40:         }
 41:       }
 42:     }
 43: 
 44:     function downloadFile() {
 45:       // FileTransferオブジェクトを作成
 46:       var fileTransfer = new FileTransfer();
 47: 
 48:       // ダウンロード先のURIをencodeURIでエンコード
 49:       var uri = encodeURI('http://image.gihyo.co.jp/assets/templates/gihyojp2007/image/gihyojp_logo.png');
 50: 
 51:       // 保存するファイルの名前
 52:       var fileName = 'gihyojp_logo.png';
 53: 
 54:       // 保存先をフルパスで指定
 55:       var path = directoryEntry.fullPath + '/' + fileName ;
 56: 
 57:       // ファイルのダウンロードを実行
 58:       fileTransfer.download(uri, path, downloadSuccess, downloadFail);
 59:     }
 60: 
 61:     function downloadSuccess(fileEntry) {
 62:       alert('ファイルを' + fileEntry.fullPath + 'に保存しました。');
 63:       location.reload();
 64:     }
 65: 
 66:     function downloadFail(fileTransferError) {
 67:       var error = '';
 68:       
 69:       switch(fileTransferError.code) {
 70:         case FileTransferError.FILE_NOT_FOUND_ERR:
 71:           error = '第2引数にファイルURLが指定されていません';
 72:           break;
 73:         case FileTransferError.INVALID_URL_ERR:
 74:           error = '不正なファイルパスが第2引数で指定されています';
 75:           break;
 76:         case FileTransferError.CONNECTION_ERR:
 77:           error = '接続エラー';
 78:           break;
 79:         default: 
 80:           error = '未定義のエラー';
 81:           break;
 82:       }
 83:       
 84:       alert(error + '\nhttp_status: ' + fileTransferError.http_status);
 85:     }
 86: 
 87:     function uploadFile(event) {
 88:       // FileTransferオブジェクトを作成
 89:       var fileTransfer = new FileTransfer();
 90: 
 91:       // アップロード先URIをencodeURIでエンコード
 92:       var uri = encodeURI('http://(ホスト名)/receive.php');
 93: 
 94:       // FileUploadOptionsオブジェクトを作成し,送信時の情報を指定
 95:       var uploadOptions = new FileUploadOptions();
 96:       uploadOptions.fileKey = 'file';
 97:       uploadOptions.fileName = event.target.textContent;
 98: 
 99:       // ファイルのアップロードを実行
100:       fileTransfer.upload(event.target.title, uri, uploadSuccess, uploadFail, uploadOptions);
101:     }
102: 
103:     function uploadSuccess(uploadResult) {
104:       var result;
105: 
106:       // アップロード先(receive.php)から返るJSONをパース
107:       if ( 'Android' === device.platform ) {
108:         result = JSON.parse(uploadResult.response);
109:       } else { 
110:         // iOSの場合はエンコードされた文字列になるため,decodeURIでデコード
111:         result = JSON.parse(decodeURI(uploadResult.response));
112:       }
113:  
114:       if ( 0 === result.errorCode ) {
115:         alert('ファイルのアップロードに成功しました\nbytesSent: ' + uploadResult.bytesSent + '\nresponseCode: ' + uploadResult.responseCode);
116:       } else {
117:         alert('ファイルのアップロードに失敗しました\n' + result.message + '\nbytesSent: ' + uploadResult.bytesSent + '\nresponseCode: ' + uploadResult.responseCode);
118:       }
119:     }
120: 
121:     function uploadFail(fileTransferError) {
122:       var error = '';
123:       
124:       switch(fileTransferError.code) {
125:         case FileTransferError.FILE_NOT_FOUND_ERR:
126:           error = '第1引数に指定したファイルが見つかりません';
127:           break;
128:         case FileTransferError.INVALID_URL_ERR:
129:           error = '不正なURIが第2引数で指定されています';
130:           break;
131:         case FileTransferError.CONNECTION_ERR:
132:           error = '接続エラー';
133:           break;
134:         default: 
135:           error = '未定義のエラー';
136:           break;
137:       }
138:       
139:       alert(error + '\nhttp_status: ' + fileTransferError.http_status);
140:     }
141: 
142:     function fail(error) {
143:       alert('エラーが発生しました。エラーコード: ' + error.code);
144:     }
145:     </script>
146:   </body>
147: </html>

リスト3 ファイル受信用のプログラム receive.php

<?php

// ファイルを受信し,JSONで結果を出力
// { errorCode : (エラーコード) , message : (メッセージ) }

// 定義したエラーコードは次のとおり
// -1: ファイルがアップロードされていない
// 0 : エラーなし
// 1 : move_uploaded_fileに失敗
// 2 : ファイル名の制限により失敗

// ファイルがアップロードされているか
if (!is_uploaded_file($_FILES['file']['tmp_name'])) {
  echo json_encode(array('errorCode' => -1, 'message' => 'ファイルがアップロードされていません'));
  exit;
}

// 拡張子が jpg, png, txt のみのファイルを受け付ける
if ( !preg_match('/\.jpg$|\.png$|\.txt$/i', $_FILES['file']['name']) ) {
  echo json_encode(array('errorCode' => 2, 'message' => '受信できるファイルの拡張子はjpg/png/txtの3種類のみです'));
  exit;
}

// アップロードされたファイルを,カレントディレクトリに移動
if (move_uploaded_file($_FILES['file']['tmp_name'], './' . str_replace('/', '', $_FILES['file']['name']))) {
  echo json_encode(array('errorCode' => 0, 'message' => 'エラーなし'));
  exit;
}
  
// エラー処理
echo json_encode(array('errorCode' => 1, 'message' => 'move_upload_fileに失敗しました。パーミッションの設定を確認してください'));
exit;

?>

前述のとおり,ダウンロード先のURLは,あらかじめCordova.plistのExternalHostsに追記を行う必要があります。ホワイトリストに定義されていないホストからファイルをダウンロードしようとした場合,FileTransferErrorオブジェクトのcodeプロパティにFileTransferError.CONNECTION_ERRが,http_statusプロパティに401が格納されます。

まずはiOSで実行してみましょう。アプリケーションを起動すると,PERSISTENTファイルシステムにファイルが格納されている場合ファイルの一覧が表示されます。また,画像をダウンロードするボタンが配置されています。

起動画面

起動画面

[download gihyo logo]ボタンをクリックすると,gihyo.jpのロゴ画像をダウンロードし,PERSISTENTファイルシステムに保存します。

ファイルをダウンロードすると,保存先のパスが表示される

ファイルをダウンロードすると,保存先のパスが表示される

PERSISTENTファイルシステムに画像ファイルが保存された

PERSISTENTファイルシステムに画像ファイルが保存された

列挙されているファイル名をクリックすると,そのファイルをreceive.phpにアップロードします。

ダウンロードしたgihyojp_logo.pngをアップロード

ダウンロードしたgihyojp_logo.pngをアップロード

receive.phpを配置したディレクトリに,ファイルがアップロードできていることが確認できた

receive.phpを配置したディレクトリに,ファイルがアップロードできていることが確認できた

PHPではファイルを受信し,move_uploaded_fileを使ってカレントディレクトリにファイルを移動しています。そのため,設置するディレクトリに書き込み権限を付加する必要があります。

JSONで結果をやり取りし,エラーコードに対応するメッセージをアラートで表示

JSONで結果をやり取りし,エラーコードに対応するメッセージをアラートで表示

存在しないサーバURLや,アップロードしたいファイルパスを指定した場合はFileTransferErrorオブジェクトが返ります。

FileTransferErrorオブジェクトの結果を利用して,エラー表示

FileTransferErrorオブジェクトの結果を利用して,エラー表示

著者プロフィール

富田宏昭(とみだひろあき)

株式会社キクミミでFileMaker/SQLiteを使ったWebアプリ開発に従事しながら,オープンソースソフトウェアのハウツー記事執筆を行う。趣味は横乗り系スポーツ,美術館めぐり,高速ジャンクション鑑賞。