Asial Blog

Recruit! Asialで一緒に働きませんか?

Ajaxによるmultipart/postでの画像ファイルアップロード

カテゴリ :
フロントエンド(HTML5)
タグ :
Monaca
PHP
JavaScript
HTML5
モバイルアプリでは、サーバーと連動して動作するものが多くみられます。
ハイブリッドアプリでこうしたアプリを作る場合は、Ajaxで実現するのが一般的だと思いますので、そのやり方について説明します。

追記:Android 2.3系およびそれ以前のAndroidは、FormDataオブジェクトが未定義のため、この記事の方法は利用出来ません。ご注意下さい。

追記2:Android 4.4では、Formからのファイル選択自体ができないため、この記事の方法は利用出来ません。(4.4.2で確認。今後のバージョンアップ出来るようになるかは不明です)
formタグを使わない手段については、こちら


サーバー側の実装



まずは、サーバー側の機能の実装です。
ここでは、普通にブラウザからもアップロード出来るような作りを考えます。実装はphpですが、他の言語でも基本的に同じように作れると思います。

/uploader.php

  1. <?php
  2.  
  3.   $textUpload = "";
  4.  
  5.   if ($_FILES['userfile']):
  6.       $uploadfile = __DIR__ . '/uploads/image.jpg';
  7.     if (move_uploaded_file($_FILES['userfile']['tmp_name'],$uploadfile)) {
  8.       $textUpload = "File is uploaded";
  9.     } else {
  10.       $textUplaod = "Upload fail";
  11.     }
  12.   endif;
  13.  
  14.   if (preg_match('/^text\/html/', $_SERVER['HTTP_ACCEPT'])) : ?>
  15.  
  16. <pre>
  17. <?php echo $textUpload; ?>
  18. </pre>
  19.  
  20. <form enctype="multipart/form-data" action="/uploader.php" method="POST">
  21.   <input name="userfile" type="file" />
  22.   <input type="submit" value="Send" />
  23. </form>
  24. <br />
  25.  
  26. Current Image:
  27. <img src="/uploads/image.jpg?<?php echo time(); ?>" width="400">
  28.  
  29. <br />
  30. <a href="/uploader.php"> REFRESH </a>
  31.  
  32. <?php else: 
  33.   header( 'Content-Type: application/json; charset=utf-8', true ); 
  34.   echo json_encode( array("message" => "Upload is OK")  );
  35.   endif; ?>

写真をおくためにuploadsディレクトリを作成して、書き込み権限を付与しておきます。
この状態で、ブラウザからアクセスすると、投稿フォームがあるので、そこから画像を
アップロードしてみて下さい。

コードの中で$_SERVER['HTTP_ACCEPT'])) を見ているのは、ブラウザフォームからの
アクセス(html/textを要求)なのか、後に行うAjaxからのアクセス(jsonを要求)なのか
を判定するためです。
なので、もしAjaxからのアクセスしか行わないのであれば、$_SERVER['HTTP_ACCEPT']))
に対するif判定でtrueの部分の処理はいりません。

アプリ側の実装



次に、Monacaから「最小限のテンプレート」でプロジェクトを作成します。
利用するプラグインとして、「jQuery」を設定します。

index.htmlのheader部のjavascriptを以下のようにします。

  1. function upload(form) {
  2.             $form = $('#upload-form');
  3.             fd = new FormData($form[0]);
  4.             $.ajax(
  5.                 'http://[サーバーのURL]/uploader.php',
  6.                 {
  7.                 type: 'post',
  8.                 processData: false,
  9.                 contentType: false,
  10.                 data: fd,
  11.                 dataType: "json",
  12.                 success: function(data) {
  13.                     alert( data.message );
  14.                     console.log(data);
  15.                 },
  16.                 error: function(XMLHttpRequest, textStatus, errorThrown) {
  17.                     alert( "ERROR" );
  18.                     alert( textStatus );
  19.                     alert( errorThrown );
  20.                 }
  21.             });
  22.             return false;
  23.         }

また、body部のhtmlは

  1. <form id="upload-form" method="post" enctype="multipart/form-data" onSubmit="return upload(this);">
  2.  <input id="upload-form-file" name="userfile" size="27" type="file" accept="image/*;capture=camera"/>
  3.  <br />
  4.  <br />
  5.  <input type="submit" name="submit" value="OK" />
  6.  </form>

とします。

これで、アプリを起動して、写真を選択、OKボタンを押すと、サーバーに接続して、写真ファイルをアップロードが行えます。アップロード後、サーバー側の「REFRESH」リンクをクリックすると、投稿した画像に更新されていることが確認出来ます。

コードの内容は、フォーム内容をFormDataクラスとして取得し、それをAjaxによりサーバーに送っているだけなので、難しいところはあまりないと思います。。dataTypeを「json」としているのは、サーバからのレスポンスを受けるにはjsonが便利だからなので、htmlやtextとして取得しても問題ありません。(success処理の実装は少し変わります)

まとめ



簡単ではありますが、Ajaxを利用して画像ファイルをアップロードするサンプルコードを紹介しました。Ajaxを利用するとページの再読み込みが必要ないので、Onsen UIなどのフレームワークからも使えます。

便利なのは、通常のフォームからデータを取得しているので、画像以外のデータも合わせて送ることが出来ること、既存のフォームにもデータを送ることが出来ることなどです。

1つ注意点としては、上記の中でフォームからアップロードする写真を選ぶ部分は、ユーザーが手動でやらなくてはならず、勝手に写真をアップロードすることは出来ません。これはセキュリティ的な制限のためです。アプリの内部に保存した画像ファイルをアップロードする場合は、Monaca (Cordova)のFileTransferを使うことになります。

サーバーと連動するアプリの開発に役立ててみて下さい。