2

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

2 Answers 2

3

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/>
Sign up to request clarification or add additional context in comments.

1 Comment

It just happened I encountered the same issue in ma current project; and you're welcome
0

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?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.