angular JSで画像をアップロードng-file-uploadの使い方と注意

ng-file-uploadを使って画像をサーバにUPしよう

Angular JSを使って、画像データをサーバにUPするには、とりあえずng-file-uploadを使いましょう。ng-file-uploadの使い方と筆者が陥ったミスをご紹介です。

ng-file-uploadのインストール

bowerをお使いならば、コマンドラインから
bower install ng-file-upload –save
とタイプします。

もしbowerを使っていないのであれば、是非このタイミングで導入をお勧めします。様々なプラグインのダウンロード、展開、紐づけ(スクリプトタグ)を、コマンド1つで全部やってくれるので、開発がすごく楽になります

その後、angular JSファイルに

angular
  .module('yo9App', ['ngFileUpload']) 

と書き足します。

htmlのコーディングin ng-file-upload

ファイルをアップロードするには、

<input type="file">

と書くのが通常のHTMLです。Angular JS + ng-file-uploadでは、次のように変わります

<form>
   <input type="file" ngf-select  ng-model="picFile" name="file"  ngf-max-size="1MB" required  ngf-accept="'image/png'" ngf-pattern="'image/png'" ngf-resize="{width: 200, height: 200}" ngf-ratio="1:1" ngf-multiple="false">
  <br>
  <button class="btn btn-success" ng-disabled="!stampUpload.$valid" ng-click="uploadPic(picFile)">送信</button>
    <button class="btn btn-warning" ng-click="picFile = null" ng-show="picFile">キャンセル</button>
</form>

えぇ、そうです。ものすごく長くなります。ただ悲観するだけではありません。ファイルのアップロードに様々な制約を課すことができるので、入口をしっかりしておけば後がラクになります。
例えばファイルのサイズ上限や、縦列比率、リサイズ後のサイズなどかなり細かく条件が指定できます。セキュリティ的には当然PHP側でも入念にチェックが必要ですが、ユーザサイドのJavaScriptでもここまでできるのは便利です。

angular JS側の処理を記述

    ///////画像アップロード処理
    $scope.uploadPic = function(item){
      Upload.upload({
          url:'ぴーえいちぴーのファイルパス/xxxxx.php',
          data: {
            loginId:$scope.loginId,
            file: item
          },
      })
      .then(function (resp){//送信成功
            $log.info("画像アップロード完了");
      },function (resp) {}
       ,function (evt)  {}
      );
    }

ジャバスクリプト側はまぁ普通な感じです

クライアントからサーバへリクエストが2回飛ぶ

ng-file-uploadは、何故かしらないけど、requestが2回送られます。色々調べたけど結局わかりませんでした。

ファイルを選択して、送信ボタンクリックすると
Request Method:OPTIONS が走ります。その後すぐ
Request Method:POST が走ります。

送信ボタン1回で、リクエストが2回送られます。1回めのoptionsのリクエストは、何が起きてるのかよくわからんのですが、実際何も起きてないようです。
実際にファイル名を取得して、色々書き換えやファイルアップロードするのは、2回目のリクエスト( POST)の方です。
この挙動のせいで5時間近くはまりました。

PHP側でangular JSから送られたデータを処理する

<?php
    $res = array("status"=>-1);//成功失敗フラグ
    $uploaddir = '../stamp/';//保存先Directory
    $uploadfile = $uploaddir . ($data['orgId']) . ".png";
    echo "orgIDは" . $data['orgId'] ;
    print_r($_FILES);

    if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
      $res['status'] = 1;
      die(json_encode($res));
    } else {
      die(json_encode($res));
    }
?>

php側では、こんな感じで書いてみました。
とりあえずファイルの書き込みが成功したら、statusが1を返し、失敗したら-1を返す。これをAngular JS側で受け取れば、成功したか、失敗したかメッセージを出力できるので、それをもとにAngular JSではメッセージを出力すればいいのです。「成功しましたよー」とか、「失敗やでー」という具合です。
ただ、何故か statusが-1ばかり返ってくる。でもちゃんとファイルはUPできていました。
ちゃんと動いているのに、 return -1 なんで???

まぁ、結論は上でも書いたように、リクエストが2回送られてるからなんです。OPTIONSのリクエストの方は、実際何のデータもまだ来てないので、当然ファイルのプットは失敗するんですね。これで -1が返ってくるんです。それを受け取ってAngular JS側で、
if(rsp[‘status’]==-1){tostr.error(“ファイル送信失敗”);
なんて書いてるので、画面上は失敗メッセージが表示されるのに、サーバを見るとちゃんとデータがあるんで、頭を抱えることになります。

とりあえず、1回めのリクエストは何の意味があるのかわからないけど、きっとファイルをアップするために必要なんでしょうね。
マニュアル見ると、リクエストを1回でやる方法?みたいなのもかいてあります。
(Upload multiple files one by one on file select)
1リクエストでファイルアップロード
ただこれ、multiple filesってことで、複数ファイルをなんかまとめて送るやり方みたいなので、わたしはスルーしましたけどね。

とりあえず、-1エラーが帰ってくるのは困るので、応急処置

    if($_SERVER['REQUEST_METHOD']=='OPTIONS'){
      die();
    }

んーこれでいいんかなぁ?エラーはでなくなったけど、意味があってリクエストしているので、これをキルしちゃうのはダメなのかも?
プラグインとか、フレームワークって独特な動きするものがあるので注意ですね。