AngularJS: bagaimana menerapkan unggahan file sederhana dengan formulir multi-bagian?

144

Saya ingin melakukan posting bentuk multi-bagian sederhana dari AngularJS ke server node.js, formulir tersebut harus berisi objek JSON di satu bagian dan gambar di bagian lain, (saya saat ini hanya memposting objek JSON dengan $ resource)

Saya pikir saya harus mulai dengan input type = "file", tetapi kemudian menemukan bahwa AngularJS tidak dapat mengikat itu ..

semua contoh yang bisa saya temukan adalah untuk membungkus plugin jQuery untuk drag & drop. Saya ingin mengunggah satu file dengan mudah.

Saya baru mengenal AngularJS dan tidak merasa nyaman sama sekali dengan menulis arahan saya sendiri.

Gal Ben-Haim
sumber
1
saya pikir ini mungkin membantu: noypi-linux.blogspot.com/2013/04/...
Noypi Gilas
1
Lihat jawaban ini: stackoverflow.com/questions/18571001/... Banyak opsi di sana untuk sistem yang sudah bekerja.
Anoyz
1
Lihat di sini
bobobobo

Jawaban:

188

Solusi kerja nyata tanpa dependensi selain angularjs (diuji dengan v.1.0.6)

html

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

Angularjs (1.0.6) tidak mendukung ng-model pada tag "input-file" sehingga Anda harus melakukannya dalam "cara asli" yang meneruskan semua (akhirnya) file yang dipilih dari pengguna.

pengontrol

$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!... );

};

Bagian kerennya adalah tipe konten yang tidak terdefinisi dan transformRequest: angular.identity yang memberi $ http kemampuan untuk memilih "tipe konten" yang tepat dan mengelola batas yang diperlukan saat menangani data multi-bagian.

Fabio Bonfante
sumber
2
@Fabio Bisakah kamu menjelaskan padaku apa yang dilakukan transformRequest ini? apa sudut pandang itu? Saya memukul-mukul kepala saya sepanjang hari hanya untuk menyelesaikan unggahan file multi-bagian.
agpt
1
@RandomUser dalam aplikasi Java Rest, sesuatu seperti itu mkyong.com/webservices/jax-rs/file-upload-example-in-jersey
Fabio Bonfante
2
wow, hanya brilian, terima kasih banyak. Di sini saya harus mengunggah beberapa gambar dan beberapa data lain juga jadi saya memanipulasi fd asfd.append("file", files[0]); fd.append("file",files[1]); fd.append("name", "MyName")
Moshii
1
console.log (fd) menunjukkan formulir kosong? apakah begitu?
J Bourne
4
Perhatikan bahwa ini tidak akan berfungsi jika Anda menonaktifkan debugInfo (seperti yang disarankan)
Bruno Peres
42

Anda dapat menggunakan arahan unggah file-ng sederhana / ringan . Ini mendukung drag & drop, kemajuan file dan upload file untuk browser non-HTML5 dengan FileAPI flash shim

<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);
  }); 

}];
danial
sumber
Bukankah ini melakukan permintaan POST tunggal untuk setiap file sekaligus?
Anoyz
Ya itu. Ada diskusi di pelacak masalah github tentang mengapa lebih baik mengunggah file satu per satu. Flash API tidak mendukung pengiriman file bersama dan AFAIK Amazon S3 juga tidak mendukungnya.
danial
Jadi, Anda mengatakan pendekatan umum yang lebih tepat adalah mengirim satu file permintaan POST sekaligus? Saya dapat melihat beberapa keuntungan di dalamnya, tetapi juga lebih banyak masalah ketika membuat dukungan server-side restful.
Anoyz
2
Cara saya menerapkannya adalah mengunggah setiap file sebagai sumber daya simpan dan server akan menyimpannya pada sistem file lokal (atau database) dan mengembalikan id unik (yaitu folder acak / nama file atau id db) untuk file itu. Kemudian setelah semua unggahan selesai, klien mengirim permintaan PUT / POST lain yang data dan id ekstra dari file yang diunggah untuk permintaan ini. Kemudian server akan menyimpan catatan dengan file terkait. Ini seperti gmail ketika Anda mengunggah file dan kemudian mengirim email.
danial
1
Ini bukan "sederhana / ringan". Bagian sampel bahkan tidak memiliki contoh mengunggah hanya satu file.
Chris
9

Lebih efisien mengirim file secara langsung.

The base64 encoding dari Content-Type: multipart/form-datamenambahkan 33% overhead tambahan. Jika server mendukungnya, akan lebih efisien untuk mengirim file secara langsung:

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

Saat mengirim POST dengan objek File , penting untuk mengatur 'Content-Type': undefined. The XHR mengirim Metode maka akan mendeteksi objek File dan secara otomatis mengatur jenis konten.

Untuk mengirim banyak file, lihat Melakukan Banyak $http.postPermintaan Secara Langsung dari FileList


Saya pikir saya harus mulai dengan input type = "file", tetapi kemudian menemukan bahwa AngularJS tidak dapat mengikat itu ..

The <input type=file>unsur tidak oleh pekerjaan default dengan ng-model yang direktif . Perlu arahan khusus :

Demo Bekerja dari "select-ng-files" Petunjuk yang Bekerja dengan 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 dengan tipe konten multipart/form-data

Jika seseorang harus mengirim 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);
};

Saat mengirim POST dengan API FormData , penting untuk diatur 'Content-Type': undefined. The XHR mengirim Metode maka akan mendeteksi FormDataobyek dan secara otomatis mengatur header jenis konten untuk multipart / form-data dengan batas yang tepat .

georgeawg
sumber
The filesInputdirektif tidak muncul untuk dapat bekerja dengan sudut 1.6.7, saya bisa melihat file dalam ng-repeattapi sama $scope.variablekosong di controller? Juga salah satu dari contoh Anda menggunakan file-modeldan satufiles-input
Dan
@Dan saya mengujinya dan berfungsi. Jika Anda memiliki masalah dengan kode Anda, Anda harus mengajukan pertanyaan baru dengan contoh Minimal, Lengkap, dan dapat diverifikasi . Mengubah nama direktif menjadi select-ng-files. Diuji dengan AngularJS 1.7.2.
georgeawg
5

Saya baru saja mengalami masalah ini. Jadi ada beberapa pendekatan. Yang pertama adalah bahwa browser baru mendukung

var formData = new FormData();

Ikuti tautan ini ke blog dengan info tentang bagaimana dukungan terbatas pada peramban modern tetapi jika tidak, ini akan menyelesaikan masalah ini sepenuhnya.

Kalau tidak, Anda dapat memposting formulir ke iframe menggunakan atribut target. Saat Anda memposting formulir, pastikan untuk mengatur target ke iframe dengan properti tampilan tidak diatur. Targetnya adalah nama iframe. (Asal kamu tahu.)

saya harap ini membantu

Edgar Martinez
sumber
AFAIK FormData tidak berfungsi dengan IE. mungkin itu ide yang lebih baik untuk melakukan encoding base64 dari file gambar dan mengirimkannya dalam JSON? bagaimana saya bisa mengikat ke input type = "file" dengan AngularJS untuk mendapatkan jalur file yang dipilih?
Gal Ben-Haim
3

Saya tahu ini entri yang terlambat, tetapi saya telah membuat arahan unggahan yang sederhana. Yang mana Anda bisa bekerja dalam waktu singkat!

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

ng-sederhana-unggah lagi di Github dengan contoh menggunakan Web API.

Shammelburg
sumber
2
sejujurnya, menyarankan untuk menyalin-menempelkan kode di readme proyek Anda bisa menjadi tanda hitam besar. coba mengintegrasikan proyek Anda dengan manajer paket umum seperti npm atau bower.
Stefano Torresi
2

Anda dapat mengunggah melalui $resourcedengan menetapkan data ke atribut params sumber daya actionsseperti:

$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
    });
};
Emeka Mbah
sumber
1

Saya baru saja menulis arahan sederhana (dari salah satu ofcourse) untuk pengunggah sederhana di AngularJs.

(Plugin pengunggah jQuery yang tepat adalah https://github.com/blueimp/jQuery-File-Upload )

Pengunggah Sederhana menggunakan AngularJs (dengan Implementasi CORS)

(Meskipun sisi server adalah untuk PHP, Anda juga dapat mengubahnya dengan mudah)

sk8terboi87 ツ
sumber
1
Jangan lupa untuk menyebutkan bahwa Anda tidak memberikan waktu untuk menutup \ menanggapi masalah pada repo Anda
Oleg Belousov
@OlegTikhonov: Ya, serius terjebak dengan beberapa proyek lain. Hampir tidak bisa fokus.
sk8terboi87 ツ