AngularJS: How To Implement A Simple File Upload With Multipart Form?
Answer :
A real working solution with no other dependencies than angularjs (tested with v.1.0.6)
html
<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>
Angularjs (1.0.6) not support ng-model on "input-file" tags so you have to do it in a "native-way" that pass the all (eventually) selected files from the user.
controller
$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!... ); };
The cool part is the undefined content-type and the transformRequest: angular.identity that give at the $http the ability to choose the right "content-type" and manage the boundary needed when handling multipart data.
You can use the simple/lightweight ng-file-upload directive. It supports drag&drop, file progress and file upload for non-HTML5 browsers with 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); }); }];
It is more efficient to send a file directly.
The base64 encoding of Content-Type: multipart/form-data
adds an extra 33% overhead. If the server supports it, it is more efficient to send the files directly:
$scope.upload = function(url, file) { var config = { headers: { 'Content-Type': undefined }, transformResponse: angular.identity }; return $http.post(url, file, config); };
When sending a POST with a File object, it is important to set 'Content-Type': undefined
. The XHR send method will then detect the File object and automatically set the content type.
To send multiple files, see Doing Multiple $http.post
Requests Directly from a FileList
I figured I should start with input type="file", but then found out that AngularJS can't bind to that..
The <input type=file>
element does not by default work with the ng-model directive. It needs a custom directive:
Working Demo of "select-ng-files" Directive that Works with ng-model
1
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
with content type multipart/form-data
If one must send 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); };
When sending a POST with the FormData API, it is important to set 'Content-Type': undefined
. The XHR send method will then detect the FormData
object and automatically set the content type header to multipart/form-data with the proper boundary.
Comments
Post a Comment