AngularJS:マルチパートフォームで単純なファイルアップロードを実装する方法?


144

AngularJSからnode.jsサーバーにシンプルなマルチパートフォームを投稿したいのですが、フォームには一方の部分にJSONオブジェクトが含まれ、もう一方の部分に画像が含まれている必要があります(現在、$ resourceを持つJSONオブジェクトのみを投稿しています)。

input type = "file"から始めるべきだと考えましたが、AngularJSがそれにバインドできないことがわかりました。

私が見つけることができるすべての例は、ドラッグアンドドロップ用のjQueryプラグインをラップするためのものです。1つのファイルを簡単にアップロードしたい。

私はAngularJSを初めて使用するので、独自のディレクティブを作成することにまったく抵抗があります。


1
私はこれが役立つと思います:noypi-linux.blogspot.com/2013/04/…– Noypi Gilas '10
07/14

1
この回答を参照してください: stackoverflow.com/questions/18571001/… すでに動作しているシステム用の多数のオプションがあります。
Anoyz、2014年

回答:


188

angularjs(v.1.0.6でテスト済み)以外の依存関係がない実際に機能するソリューション

html

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>

Angularjs(1.0.6)は「input-file」タグでng-modelをサポートしていないため、ユーザーから(最終的に)選択されたすべてのファイルを渡す「ネイティブ方式」で行う必要があります。

コントローラ

$scope.uploadFile = function(files) {
    var fd = new FormData();
    //Take the first selected file
    fd.append("file", files[0]);

    $http.post(uploadUrl, fd, {
        withCredentials: true,
        headers: {'Content-Type': undefined },
        transformRequest: angular.identity
    }).success( ...all right!... ).error( ..damn!... );

};

クールな部分は、未定義のコンテンツタイプとtransformRequest:angular.identityで、$ httpで正しい「コンテンツタイプ」を選択し、マルチパートデータを処理するときに必要な境界を管理する機能を提供します。


2
@FabioこのtransformRequestは何をするのですか?angular.identityとは何ですか?マルチパートファイルのアップロードを完了するために、1日中頭を悩ませていました。
agpt 2014

1
Java Restアプリケーションの@RandomUser、そのようなものmkyong.com/webservices/jax-rs/file-upload-example-in-jersey
Fabio

2
うわー、ただ素晴らしい、本当にありがとう。ここでは、複数の画像やその他のデータもアップロードする必要があるので、fdを次のように操作しますfd.append("file", files[0]); fd.append("file",files[1]); fd.append("name", "MyName")
Moshii

1
console.log(fd)はフォームが空であることを示していますか?そうですか?
Jボーン

4
debugInfoを無効にしている場合(推奨どおり)、これは機能しないことに注意してください。
Bruno Peres

42

シンプル/軽量を使用できます ng-file-uploadディレクティブ。FileAPIフラッシュシムを使用して、HTML5以外のブラウザーのドラッグアンドドロップ、ファイルの進行状況、ファイルのアップロードをサポートします。

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>

JS:

//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);

var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
  $scope.onFileSelect = function($files) {
  Upload.upload({
    url: 'my/upload/url',
    file: $files,            
  }).progress(function(e) {
  }).then(function(data, status, headers, config) {
    // file is uploaded successfully
    console.log(data);
  }); 

}];

これは一度に各ファイルに対して単一のPOSTリクエストを実行していませんか?
Anoyz

はい、そうです。github issue trackerには、ファイルを1つずつアップロードする方がよい理由についての議論があります。Flash APIはファイルの送信をサポートしておらず、AFAIK Amazon S3もそれをサポートしていません。
2013

それで、あなたはより一般的なより正しいアプローチは一度に1つのファイルPOSTリクエストを送ることだと言っていますか?これにはいくつかの利点がありますが、サーバー側の安らかなサポートを作成するときにさらに問題が発生します。
Anoyz

2
それを実装する方法は、リソースの保存として各ファイルをアップロードすることであり、サーバーはローカルファイルシステム(またはデータベース)にファイルを保存し、そのファイルの一意のID(つまり、ランダムなフォルダー/ファイル名またはDB ID)を返します。次に、すべてのアップロードが完了すると、クライアントは別のPUT / POSTリクエストを送信します。このリクエストのためにアップロードされる追加のデータとファイルのIDが追加されます。次に、サーバーはレコードを関連ファイルとともに保存します。ファイルをアップロードしてメールを送信すると、Gmailのようになります。
2013

1
これは「単純/軽量」ではありません。サンプルセクションには、1つのファイルのみをアップロードする例もありません。
クリス・

9

ファイルを直接送信する方が効率的です。

base64エンコードContent-Type: multipart/form-dataは、33%のオーバーヘッドが追加されます。サーバーがサポートしている場合は、ファイルを直接送信する方が効率的です。

$scope.upload = function(url, file) {
    var config = { headers: { 'Content-Type': undefined },
                   transformResponse: angular.identity
                 };
    return $http.post(url, file, config);
};

FileオブジェクトでPOSTを送信する場合は、設定することが重要'Content-Type': undefinedです。XHRのsendメソッドは、その後、検出しますFileオブジェクトを、自動的にコンテンツタイプを設定します。

複数のファイルを送信するには、FileListから直接複数の要求行うを参照してください$http.post


input type = "file"から始めるべきだと考えましたが、AngularJSがそれにバインドできないことがわかりました。

この<input type=file>要素は、デフォルトではng-modelディレクティブでは機能しません。カスタムディレクティブが必要ですです。

で動作する「select-ng-files」ディレクティブの作業デモng-model1

angular.module("app",[]);

angular.module("app").directive("selectNgFiles", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" select-ng-files ng-model="fileArray" multiple>
    
    <h2>Files</h2>
    <div ng-repeat="file in fileArray">
      {{file.name}}
    </div>
  </body>


$http.post コンテンツタイプ multipart/form-data

送信する必要がある場合multipart/form-data

<form role="form" enctype="multipart/form-data" name="myForm">
    <input type="text"  ng-model="fdata.UserName">
    <input type="text"  ng-model="fdata.FirstName">
    <input type="file"  select-ng-files ng-model="filesArray" multiple>
    <button type="submit" ng-click="upload()">save</button>
</form>
$scope.upload = function() {
    var fd = new FormData();
    fd.append("data", angular.toJson($scope.fdata));
    for (i=0; i<$scope.filesArray.length; i++) {
        fd.append("file"+i, $scope.filesArray[i]);
    };

    var config = { headers: {'Content-Type': undefined},
                   transformRequest: angular.identity
                 }
    return $http.post(url, fd, config);
};

FormData APIでPOSTを送信する場合は、設定することが重要'Content-Type': undefinedです。XHRの送信方法は、次いで、検出しFormDataたオブジェクトおよび自動的にコンテンツタイプヘッダに設定マルチパート/フォームデータを用いて適切な境界


filesInputディレクティブは、私は内のファイルを参照することができ、角度1.6.7で作業していないと思われるng-repeatが、同じでは$scope.variable、コントローラに空白になって?また、使用file-modelしている例の1つと1つfiles-input
Dan

@ダン私はそれをテストし、それは動作します。コードに問題がある場合は、最小限の完全な検証可能な例を使用して、新しい質問をする必要があります。ディレクティブ名をに変更しましたselect-ng-files。AngularJS 1.7.2でテスト済み。
georgeawg 2018

5

この問題が発生しました。したがって、いくつかのアプローチがあります。1つ目は、新しいブラウザが

var formData = new FormData();

についての情報をブログにこのリンクに従ってくださいサポートが最新のブラウザーにどのように制限されているかについてのが記載され。

それ以外の場合は、target属性を使用してフォームをiframeに送信できます。フォームを投稿するときは、ターゲットをiframeに設定し、displayプロパティをnoneに設定してください。ターゲットはiframeの名前です。(ちょうどあなたが知っているので。)

これが役に立てば幸い


AFAIK FormDataはIEでは機能しません。画像ファイルのbase64エンコーディングを行い、それをJSONで送信する方が良いかもしれません。AngularJSで入力type = "file"にバインドして、選択したファイルパスを取得するにはどうすればよいですか?
Gal Ben-Haim、

3

これは遅いエントリであることは知っていますが、簡単なアップロードディレクティブを作成しました。あなたはすぐに働くことができます!

<input type="file" multiple ng-simple-upload web-api-url="/api/post"
       callback-fn="myCallback" />

Web APIを使用した例を使用して、Githubにng-simple-uploadを追加します。


2
正直に言うと、プロジェクトのREADMEにコードをコピーして貼り付けるよう提案することは、大きなブラックマークになる可能性があります。プロジェクトをnpmやbowerなどの一般的なパッケージマネージャーと統合してみてください。
Stefano Torresi

2

次のように$resource、リソースのparams属性にデータを割り当てることでvia をアップロードできますactions

$scope.uploadFile = function(files) {
    var fdata = new FormData();
    fdata.append("file", files[0]);

    $resource('api/post/:id', { id: "@id" }, {
        postWithFile: {
            method: "POST",
            data: fdata,
            transformRequest: angular.identity,
            headers: { 'Content-Type': undefined }
        }
    }).postWithFile(fdata).$promise.then(function(response){
         //successful 
    },function(error){
        //error
    });
};

1

AngularJsのシンプルなアップローダー用の(既存のコースからの)シンプルなディレクティブを書いたところです。

(正確なjQueryアップローダープラグインはhttps://github.com/blueimp/jQuery-File-Uploadです

AngularJsを使用したシンプルなアップローダー(CORS実装あり)

(サーバー側はPHP用ですが、ノードを簡単に変更することもできます)


1
リポジトリの問題に対処する\応答するまでの時間を与えないことを忘れないでください
Oleg Belousov 2013年

@OlegTikhonov:ええ、他のいくつかのプロジェクトと真剣に立ち往生しています。集中できません。
sk8terboi87ツオクト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.