AngularJS: จะใช้การอัปโหลดไฟล์อย่างง่ายด้วยฟอร์มหลายส่วนได้อย่างไร?


144

ฉันต้องการทำโพสต์รูปแบบหลายส่วนอย่างง่ายจาก AngularJS ไปยังเซิร์ฟเวอร์ node.js รูปแบบควรมีวัตถุ JSON ในส่วนหนึ่งและภาพในส่วนอื่น ๆ (ฉันกำลังโพสต์เฉพาะวัตถุ JSON กับ $ ทรัพยากร)

ฉันคิดว่าฉันควรเริ่มต้นด้วย input type = "file" แต่ก็พบว่า AngularJS ไม่สามารถผูกกับสิ่งนั้นได้ ..

ตัวอย่างทั้งหมดที่ฉันสามารถหาได้สำหรับการรวมปลั๊กอิน jQuery สำหรับการลากและวาง ฉันต้องการอัพโหลดไฟล์เดียวง่ายๆ

ฉันใหม่กับ AngularJS และไม่รู้สึกสะดวกสบายเลยกับการเขียนคำสั่งของฉันเอง


1
ฉันคิดว่านี่อาจช่วยได้: noypi-linux.blogspot.com/2013/04/…
Noypi Gilas

1
ดูคำตอบนี้: stackoverflow.com/questions/18571001/… มีตัวเลือกมากมายสำหรับระบบการทำงานที่มีอยู่แล้ว
Anoyz

คำตอบ:


188

วิธีแก้ปัญหาการทำงานจริงที่ไม่มีการพึ่งพาอื่นนอกเหนือจาก angularjs (ทดสอบด้วย v.1.0.6)

HTML

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

Angularjs (1.0.6) ไม่รองรับng-modelบนแท็ก "input-file" ดังนั้นคุณต้องทำมันใน "native-way" ที่ส่งไฟล์ที่เลือกทั้งหมด (ในที่สุด) จากผู้ใช้

ตัวควบคุม

$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 นี้ทำอะไรได้บ้าง? สิ่งที่เป็นมุม. เหตุการณ์คืออะไร? ฉันต่อสู้ตลอดทั้งวันเพื่อให้การอัปโหลดไฟล์หลายส่วนสำเร็จ
agpt

1
@RandomUser ในแอปพลิเคชันที่เหลือของจาวาสิ่งที่คล้ายกันคือmkyong.com/webservices/jax-rs/file-upload-example-in-jersey
Fabio Bonfante

2
ว้าวยอดเยี่ยมมากขอบคุณมาก ที่นี่ฉันต้องอัปโหลดภาพหลายภาพและข้อมูลอื่น ๆ ด้วยดังนั้นฉันจัดการ fd เป็นfd.append("file", files[0]); fd.append("file",files[1]); fd.append("name", "MyName")
Moshii

1
console.log (fd) แสดงว่าฟอร์มว่างเปล่า? มันเป็นอย่างนั้น?
J Bourne

4
โปรดทราบว่านี้จะไม่ทำงานถ้าคุณมีdebugInfoคนพิการ (ตามคำแนะนำ)
บรูโน่เปเรส

42

คุณสามารถใช้คำสั่งng-file-uploadง่าย ๆ / น้ำหนักเบา รองรับการลากและวาง, ความคืบหน้าของไฟล์และการอัปโหลดไฟล์สำหรับเบราว์เซอร์ที่ไม่ใช่ HTML5 พร้อม 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);
  }); 

}];

นี่ไม่ใช่การร้องขอ POST เดียวสำหรับแต่ละไฟล์ใช่ไหม
Anoyz

ใช่แล้ว. มีการอภิปรายในตัวติดตามปัญหา github เกี่ยวกับสาเหตุที่ดีกว่าในการอัปโหลดไฟล์ทีละไฟล์ Flash API ไม่รองรับการส่งไฟล์ร่วมกันและ AFAIK Amazon S3 ไม่รองรับเช่นกัน
dialial

ดังนั้นคุณกำลังบอกว่าวิธีการทั่วไปที่ถูกต้องมากกว่านี้คือการส่งคำขอ POST หนึ่งไฟล์ต่อครั้ง? ฉันเห็นข้อดีหลายประการในนั้น แต่ยังมีปัญหามากกว่าเมื่อทำการสนับสนุนด้านเซิร์ฟเวอร์
Anoyz

2
วิธีที่ฉันใช้คือการอัปโหลดแต่ละไฟล์เป็นทรัพยากรที่บันทึกและเซิร์ฟเวอร์จะบันทึกไว้ในระบบไฟล์โลคัล (หรือฐานข้อมูล) และส่งคืนรหัสเฉพาะ (เช่นโฟลเดอร์สุ่ม / ชื่อไฟล์หรือ db id) สำหรับไฟล์นั้น จากนั้นเมื่ออัพโหลดเสร็จแล้วไคลเอนต์จะส่งคำขอ PUT / POST อีกครั้งซึ่งข้อมูลเพิ่มเติมและรหัสของไฟล์ที่อัพโหลดสำหรับคำขอนี้ จากนั้นเซิร์ฟเวอร์จะบันทึกข้อมูลด้วยไฟล์ที่เกี่ยวข้อง มันเหมือนกับ gmail เมื่อคุณอัปโหลดไฟล์แล้วส่งอีเมล
dialial

1
นี่ไม่ใช่ "ง่าย / น้ำหนักเบา" ส่วนตัวอย่างไม่มีตัวอย่างของการอัปโหลดเพียงไฟล์เดียว
Chris

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

เมื่อมีการส่งโพสต์กับเป็นวัตถุไฟล์'Content-Type': undefinedมันเป็นสิ่งสำคัญที่จะชุด วิธี XHR ส่งแล้วจะตรวจสอบวัตถุไฟล์โดยอัตโนมัติและกำหนดชนิดของเนื้อหา

หากต้องการส่งหลายไฟล์โปรดดูการทำหลาย$http.postคำขอโดยตรงจาก FileList


ฉันคิดว่าฉันควรเริ่มต้นด้วย input type = "file" แต่ก็พบว่า AngularJS ไม่สามารถผูกกับสิ่งนั้นได้ ..

<input type=file>องค์ประกอบไม่ได้เริ่มต้นจากการทำงานกับNG-รูปแบบคำสั่ง มันต้องการคำสั่งที่กำหนดเอง :

สาธิตการทำงานของคำสั่ง "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'Content-Type': undefinedมันเป็นสิ่งสำคัญที่จะชุด วิธี XHR ส่งแล้วจะตรวจสอบFormDataวัตถุโดยอัตโนมัติและการตั้งค่าส่วนหัวของชนิดของเนื้อหาที่จะmultipart / form ข้อมูลที่มีขอบเขตที่เหมาะสม


filesInputสั่งไม่ปรากฏที่จะทำงานกับเชิงมุม 1.6.7 ฉันสามารถดูไฟล์ในng-repeatเดียวกัน แต่$scope.variableว่างเปล่าในการควบคุมหรือไม่ นอกจากนี้หนึ่งในตัวอย่างของคุณใช้file-modelและอีกอย่างหนึ่งfiles-input
แดน

@Dan ฉันทดสอบและใช้งานได้ หากคุณมีปัญหาเกี่ยวกับรหัสของคุณคุณควรถามคำถามใหม่ด้วยตัวอย่างที่น้อยที่สุดสมบูรณ์ตรวจสอบได้ select-ng-filesเปลี่ยนชื่อสั่งการ ทดสอบกับ AngularJS 1.7.2
georgeawg

5

ฉันเพิ่งมีปัญหานี้ ดังนั้นจึงมีวิธีการไม่กี่ อย่างแรกคือเบราว์เซอร์ใหม่รองรับ

var formData = new FormData();

ไปที่ลิงก์นี้ไปยังบล็อกที่มีข้อมูลเกี่ยวกับการสนับสนุนที่ จำกัด เฉพาะเบราว์เซอร์สมัยใหม่ แต่ไม่เช่นนั้นจะสามารถแก้ไขปัญหานี้ได้

มิฉะนั้นคุณสามารถโพสต์แบบฟอร์มไปยัง iframe โดยใช้แอตทริบิวต์ target เมื่อคุณโพสต์แบบฟอร์มให้แน่ใจว่าตั้งค่าเป้าหมายเป็น iframe ด้วยคุณสมบัติการแสดงผลที่ตั้งค่าเป็นไม่มี เป้าหมายคือชื่อของ iframe (เพียงเพื่อให้คุณรู้ว่า.)

ฉันหวังว่านี่จะช่วยได้


AFAIK FormData ไม่ทำงานกับ IE อาจเป็นความคิดที่ดีกว่าในการเข้ารหัส base64 ของไฟล์รูปภาพและส่งใน JSON ฉันจะผูกเข้ากับ input type = "file" กับ AngularJS เพื่อรับพา ธ ไฟล์ที่เลือกได้อย่างไร
Gal Ben-Haim

3

ฉันรู้ว่านี่เป็นรายการล่าช้า แต่ฉันได้สร้างคำสั่งอัพโหลดอย่างง่าย ซึ่งคุณสามารถทำงานได้ในเวลาไม่นาน!

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

ng-simple-uploadเพิ่มเติมบน Github พร้อมตัวอย่างโดยใช้ Web API


2
ความซื่อสัตย์แนะนำให้คัดลอกรหัสวางใน readme โครงการของคุณอาจเป็นเครื่องหมายสีดำขนาดใหญ่ ลองรวมโครงการของคุณกับผู้จัดการแพ็คเกจทั่วไปเช่น npm หรือ bower
Stefano Torresi

2

คุณสามารถอัปโหลดผ่านได้$resourceโดยกำหนดข้อมูลให้กับแอตทริบิวต์ของทรัพยากร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

@OlegTikhonov: ใช่อย่างจริงจังกับโครงการอื่น ๆ ไม่สามารถโฟกัสได้
sk8terboi87 ツ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.