AngularJS Upload and Post Multiple Files

All we need is an easy explanation of the problem, so here it is.

So ultimately, what I’m doing is trying to upload and post multiple files to a Django backend using AngularJS. I can post a single file, but it seems like when a FileList object is put in the $http.post data field, the backend no longer detects any files in the POST request.

Here’s what the html looks like:

<form enctype="multipart/form-data" action="upload" method="POST">
  <div class="form-group">
    <span class="btn btn-info btn-file">
      <i class="glyphicon glyphicon-file"></i> Browse
      <input class="btn btn-lg btn-info" name="uploadedfile" type="file" accept=".eossa" onchange="angular.element(this).scope().filesUploaded(this.files)" multiple><br/>
    </span>
    <button type="button" class="btn btn-success" ng-click="uploadFiles()" ng-class="{'disabled':!model.files.length}">
      <i class="glyphicon glyphicon-download-alt"></i> Submit
    </button>
  </div>
  <pre ng-repeat="file in model.files" ng-show="file.name">{{file.name}}  ({{file.size/1000}} KB)  {{file.lastModifiedDate}} </pre>
</form>

And here’s the relevant JavaScript:

$scope.filesUploaded = function(files) {
    if(files.length < 1) return;
    $scope.model.files = files;
    $scope.$apply();
};

$scope.uploadFiles = function(){
  var fd = new FormData();
  fd.append("file", $scope.model.files);
  Services.uploadFiles(fd)}

Here’s my uploadFiles service:

uploadFiles: function(form){
    return $http.post("/api/file-upload/", form, {withCredentials: true, headers: {'Content-Type': undefined }, transformRequest: angular.identity})
  },

The backend does not see anything in the request.FILES in this case; however, if instead of appending $scope.model.files, I append $scope.model.file[0], I do see a file in the request on the backend. In which case, the uploadFiles function looks like this:

$scope.uploadFiles = function(){
  var fd = new FormData();
  fd.append("file", $scope.model.files[0]);
  Services.uploadFiles(fd)
}

So why can’t one append a FileList to a Form? How can you POST a FileList?

Thanks

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

First create a directive as pointed out here

.directive('filesModel', function () {
    return {
        restrict: 'A',
        controller: function ($parse, $element, $attrs, $scope) {
            var exp = $parse($attrs.filesModel);
            $element.on('change', function () {
                exp.assign($scope, this.files);
                $scope.$apply();
            });
        }
    };
});

And for the transform function, check this out.
You can use a factory like:

    .factory('File', function () {
        return {
            // Define a function to transform form data
            transformRequest: function (data, headersGetter) {
                var fd = data ? new FormData() : null;
                if (data) {
                    angular.forEach(data, function (value, key) {
                        // Is it a file?
                        if (value instanceof FileList) {
                            if (value.length == 1) {
                                fd.append(key, value[0]);
                            } else {
                                angular.forEach(value, function (file, index) {
                                    fd.append(key + '_' + index, file);
                                });
                            }
                        }
                        // Is it an object?
                        else if (typeof value === 'object') {
                            fd.append(key, JSON.stringify(value));
                        } else {
                            fd.append(key, value);
                        }
                    });
                }
                return fd;
            }
        };
    })

Then for the service:

uploadFiles: function(form){
    return $http.post("/api/file-upload/", form, {withCredentials: true, headers: {'Content-Type': undefined }, transformRequest: File.transformRequest})
}

Finally the html:

<input type="file" files-model="<model>"/>

Or this for multiple files

<input type="file" files-model="<model>" multiple/>

Method 2

I’ve never tried the method above and this may be a cop out but I thought I would turn you on to some libraries.

I would checkout these two awesome AngularJS file upload Repos. I personally use this one.

Both support easy setup multiple file upload.

ng-file-upload

Here’s another.

angular-file-upload

Why put in hours of work when dozens of folks have contributed and done it for you?

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply