File API紹介の最終回となる今回は、FileTransferオブジェクトを使って、ファイルのダウンロード・アップロードを行う手順を紹介していきます。性質上、Webサーバとファイルを受信するプログラムが別途必要になりますのであらかじめご準備ください。
FileTransferオブジェクトを使ったファイルダウンロード・アップロード
FileTransferオブジェクトを使用して、ファイルをダウンロード・アップロードするアプリケーションを作成してみましょう。
新たに登場するオブジェクトとメソッドは次のとおりです。
- FileTransferオブジェクト
- FileTransferErrorオブジェクト
- FileUploadOptionsオブジェクト
- FileUploadResultオブジェクト
- downloadメソッド
- uploadメソッド
FileTransfer/FileTransferError/FileUploadOptions/ FileUploadResultオブジェクトとは
選択したファイルを外部サーバに送信するサンプルプログラムを紹介する前に、ファイル転送に関係する4種類のオブジェクトについて確認しておきましょう。
FileTransferオブジェクトは、サーバからファイルをダウンロード、またはファイルをサーバにアップロードする際に使用するオブジェクトです。
FileTransferオブジェクトにプロパティはありません。用意されているメソッドは次のとおりです(使い方のfileTransferは、FileTransferオブジェクトを指します)。
メソッド名 | 内容 |
使い方 |
download | 指定したサーバからファイルをダウンロードします。
- ダウンロードしたいファイルのURLをsourceに指定します。encodeURI()メソッドでエンコードされている必要があります。
- ファイルの保存先をtargetにフルパス表記で指定します。
- targetに存在しないディレクトリを指定した場合は、ダウンロード成功時に自動的に作成されます。
- ダウンロードしたいファイルのホストを、あらかじめホワイトリストに定義しておく必要があります。
- 成功時、successCallbackに、取得したファイルの情報をFileEntryオブジェクトして渡します。
- 失敗時、errorCallbackに、FileTransferErrorオブジェクトを渡します。
|
fileTransfer.download(source, target, successCallback, errorCallback); |
upload | 指定したサーバにファイルをアップロードします。
- アップロードしたいファイルのフルパスをfilePathに指定します。
- ファイルの送信先URLをserverに指定します。encodeURI()メソッドでエンコードされている必要があります。
- ファイル名やMIME、パラメータなどの情報をFileUploadOptionsオブジェクトとして定義し、optionsに指定します。
- すべてのホスト(自己署名の証明書を使用しているホストなど)を信頼する場合は、trustAllHostsにtrueを指定します。デフォルト値はfalseです(執筆時点で、この指定は無視される模様です)
- 成功時、successCallbackに、FileUploadResultオブジェクトして渡します。
- 失敗時、errorCallbackに、FileTransferErrorオブジェクトを渡します。
|
fileTransfer.upload(filePath, server, successCallback, errorCallback, options, trustAllHosts); |
FileTransferErrorオブジェクトは、FileTransferオブジェクトでダウンロードまたはアップロードの際にエラーが発生した際に渡されるエラーオブジェクトです。メソッドはありません。
FileTransferErrorオブジェクトのプロパティは次のとおりです。
- code
- 対応するエラーコードを返します
- source
- ソースのURIを返します
- target
- ターゲットのURIを返します
- http_status
- HTTPステータスコードを返します(レスポンスコードがHTTP接続から返されたときのみ有効です)
対応する定数とエラーコードは次のとおりです。
- FileTransferError.FILE_NOT_FOUND_ERR
- ダウンロード時の第2引数に指定したパスがファイルURLでない場合や、アップロード時の第1引数に指定したファイルが見つからない場合に返ります
- FileTransferError.INVALID_URL_ERR
- ダウンロード時・アップロード時ともに第2引数に不正なファイルURLを指定した場合に返ります
- FileTransferError.CONNECTION_ERR
- ネットワーク接続がない場合や、処理完了時のレスポンスコードが200~300の範囲でなかった場合に返ります
FileTransferオブジェクトのcodeプロパティと、http_statusプロパティは連動していません。たとえばサーバ側で404エラーが返るURLをdownloadメソッドで指定した場合、codeプロパティはFileTransferError.FILE_NOT_FOUND_ERRではなくFileTransferError.CONNECTION_ERRを、http_statusは404の値を取ります。若干分かりづらいので、ご自身の手で後述のサンプルをカスタマイズし、エラーの値を確認してみてください。
FileUploadOptionsは、FileTransferオブジェクトのuploadメソッド時に指定するオプションをまとめたオブジェクトです。メソッドはありません。
FileUploadOptionsのプロパティは次のとおりです。
- fileKey
- Content-Dispositionヘッダのname属性に指定する名前を指定します。デフォルト値はnullです
- fileName
- Content-Dispositionヘッダのfilename属性に指定する名前を指定します。デフォルト値はnullです
- mimeType
- Content-Typeヘッダに指定するMIMEタイプを指定します。デフォルト値はnullです。指定しなかった場合、Content-Typeヘッダは送信されません
- params
- HTTPリクエストに含めたいデータをkey/value形式で指定します
- chunkedMode
- チャンク・ストリーミングでファイルを送信したい場合はtrueを指定します(執筆時点で、この指定は無視される模様です)
FileUploadResultオブジェクトは、FileTransferオブジェクトのuploadメソッドでファイルアップロードに成功した場合に渡されるオブジェクトです。メソッドはありません。
FileUploadResultオブジェクトのプロパティは次のとおりです。
- bytesSent
- 送信されたバイト数を示します
- responseCode
- サーバから返されたHTTPレスポンスコードを示します
- response
- サーバから返されたHTTPレスポンスを示します
iOSの場合、responseにはstringByAddingPercentEscapesUsingEncodingでエンコードされた文字列が格納されます。JSONをやりとりするアプリケーションを作成する場合、そのままではJSON.parseが動作しないので注意しましょう。Androidの場合、エンコードは行われません。
また、現在の最新バージョンであるPhoneGap 2.1.0では、ファイル転送に関係するオブジェクトにおいてFileWriterオブジェクトやFileReaderオブジェクトにあるような「ファイルのダウン/アップロード中」や「ファイルの送信を中止させる」に相当するイベント・メソッドが用意されていません。そのため、ダウンロード・アップロードの進捗率を画面に表示するといったことは難しいでしょう。
ダウンロード・アップロードの進捗イベントを捕捉するprogressイベントや送信・受信を中止するabortメソッドについては、PhoneGap 2.2.0にてサポートされる予定です。これらの動向について詳しく確認したい方は、Apache JIRAの[#CB-622]FileTransfer interface should provide progress monitoringをチェックしておきましょう。
ファイル転送に関係するオブジェクトをまとめると、次のようになります。
オブジェクト名 | 内容 |
FileTransfer | ファイルのダウンロード・アップロードを行う。
- アップロード成功時に、FileUploadResultオブジェクトを返す
- ダウンロード成功時に、ダウンロードしたファイルのFileEntryオブジェクトを返す
- ダウンロード/アップロード失敗時にFileTransferErrorオブジェクトを返す
|
FileTransferError | FileTransferオブジェクトのdownload/uploadメソッド使用時、失敗した際に返るエラーオブジェクト |
FileUploadOptions | FileTransferオブジェクトのuploadメソッド使用時、アップロード時のHTTPヘッダに付加したい情報を定義するためのオブジェクト |
FileUploadResult | FileTransferオブジェクトのuploadメソッド使用時、成功した際の送信バイト数/サーバから返されたレスポンスをまとめたオブジェクト |
ファイルのダウンロード・アップロード(iOS/Android)
ファイルのダウンロード・アップロードを行うアプリケーションを作成します。要件は次のとおりです。
- ダウンロードするファイルは、gihyo.jpのロゴ。PERSISTENTファイルシステムに保存
- PERSISTENTファイルシステムからファイルを選択し、アップロード
- アップロード先には、ファイルを受信してカレントディレクトリに保存するプログラムを指定する。PHPで実装
前述のとおり、ダウンロード先のURLは、あらかじめCordova.plistのExternalHostsに追記を行う必要があります。ホワイトリストに定義されていないホストからファイルをダウンロードしようとした場合、FileTransferErrorオブジェクトのcodeプロパティにFileTransferError.CONNECTION_ERRが、http_statusプロパティに401が格納されます。
まずはiOSで実行してみましょう。アプリケーションを起動すると、PERSISTENTファイルシステムにファイルが格納されている場合ファイルの一覧が表示されます。また、画像をダウンロードするボタンが配置されています。
[download gihyo logo]ボタンをクリックすると、gihyo.jpのロゴ画像をダウンロードし、PERSISTENTファイルシステムに保存します。
列挙されているファイル名をクリックすると、そのファイルをreceive.phpにアップロードします。
PHPではファイルを受信し、move_uploaded_fileを使ってカレントディレクトリにファイルを移動しています。そのため、設置するディレクトリに書き込み権限を付加する必要があります。
存在しないサーバURLや、アップロードしたいファイルパスを指定した場合はFileTransferErrorオブジェクトが返ります。
続いて、Androidで実行してみます。
このサンプルコードでは、前回の「PERSISTENTファイルシステムのファイル一覧を表示。ファイル名をクリックして、ファイル内のデータを表示するサンプル」をベースに、ファイルのダウンロードとアップロードを行います。行っている処理の流れは次のとおりです。
- PERSISTENTファイルシステムに格納されているファイル一覧を表示
- [download gihyo logo]ボタンをクリックすると、指定したURIから画像ファイルをダウンロードする
- ファイル名をクリックすると、そのファイルを指定したURIにアップロードする
まずはファイルのダウンロードを行っているコードから確認してみましょう。
46行目でFileTransferオブジェクトをあたらしく作成します。ダウンロードしたいURIをencodeURIメソッドを使ってエンコードし、ファイル名を決めておきます。ファイルのダウンロード先はファイル名を含めたフルパスで指定する必要があります。55行目でdirectoryEntryオブジェクトのfullPathプロパティと、52行目で宣言したfileName変数を連結します。
58行目でファイルのダウンロードを行います。あらかじめ、PhoneGapプロジェクトにおいてダウンロード先のホストをホワイトリスト(ExternalHosts)に追加しておく必要があります。本連載の第3回目にてiOS/Android環境下におけるExternalHostsの追記方法を紹介していますので、併せてご参照ください。
ダウンロードに成功した場合は、第3引数に指定した関数にダウンロードしたファイルのFileEntryオブジェクトが渡されます。
fileEntryオブジェクトのfullPathプロパティに保存したファイルのフルパスが格納されていますので、アラートで表示し、画面を再描画します。
ダウンロードに失敗した場合は、第4引数に指定した関数にFileTransferErrorオブジェクトが渡されます。
続いてアップロードを行っているコードを確認してみましょう。
FileTransferオブジェクトを作成し、アップロード先URIをencodeURIでエンコードします。95~97行目でFileUploadOptionsオブジェクトを作成し、ファイル送信時の情報を指定します。fileKeyにfileを、fileNameに<li>要素のtextContentプロパティを指定しました。
たとえばファイル一覧においてgihyojp_logo.pngをクリックしてアップロードを行った場合、アップロード先には次の情報が送信されます。
100行目でファイルのアップロードを行います。アップロード先のホストは、ホワイトリスト(ExternalHosts)に追記する必要はありません。
アップロードに成功した場合は、第3引数に指定した関数にアップロードの結果を格納したFileUploadResultオブジェクトが渡されます。
receive.phpからはJSON形式の文字列で結果が返るようになっています。FileUploadResultオブジェクトのresponseプロパティに、サーバから受信したデータが格納されますので、このデータをJSON.parseでパースします。
繰り返しになりますが、iOSの場合、responseにはstringByAddingPercentEscapesUsingEncodingでエンコードされた文字列が格納されます。そこでDevice APIをもちいて、Androidの場合は返り値をそのまま、iOSの場合はいったんdecodeURIでデコードした上でパースを行うようにしています。
JSONで渡されたエラーコードを読み取り、結果をアラートで表示します。
アップロードに失敗した場合は、第4引数に指定した関数にFileTransferErrorオブジェクトが渡されます。
FileTransferErrorオブジェクトのエラー内容ついて、ダウンロード時・アップロード時においてそれぞれエラーの内容が区別されていることに注意してください。サンプルコードを改変し、「実際には存在しないサーバURL」「実際には存在しないファイルパス」「サンドボックス外のファイルパス」「間違ったファイルパス表記」などを指定してみて、実際のエラー表示を確かめておくことをおすすめします。
また、作成するアプリケーション上でFile APIのdownloadメソッドを取り扱う際は注意してください。ExternalHostsですべてのホストからのリソース取得を許可する「*」を指定し、任意の場所にファイルを保存できるようなユーザインタフェースの場合、重大なセキュリティホールになり得ます。アプリケーションの動作確認時でも、ExternalHostsには信頼できるホスト名のみを記述しておくべきです。
以上、5回にわたってFile APIについての紹介をしてきました。ローカルデバイス内のファイル操作や、リモートから・リモートへのファイル転送が要求される場面は多岐に渡ります。ファイル転送中のイベントハンドラが現時点で未サポートなのは痛いところですが、JavaScriptのみで各スマートフォンOSのファイルシステムを操作できるのは魅力的です。
メソッドの返り値や挙動など、公式ドキュメントと併せてW3Cの仕様書(File API, File API: Directories and System)やPhoneGap(Cordova)の実装コードを見ながら開発を行わないと思いがけないところでハマる可能性もあります。本連載がPhoneGap開発の助けになれば幸いです。