Angularjs how to upload multipart form data and a file?

我怕爱的太早我们不能终老 提交于 2019-11-26 19:37:17
Prasad Shinde

First of all

  1. You don't need any special changes in the structure. I mean: html input tags.

<input accept="image/*" name="file" ng-value="fileToUpload"
       value="{{fileToUpload}}" file-model="fileToUpload"
       set-file-data="fileToUpload = value;" 
       type="file" id="my_file" />

1.2 create own directive,

.directive("fileModel",function() {
	return {
		restrict: 'EA',
		scope: {
			setFileData: "&"
		},
		link: function(scope, ele, attrs) {
			ele.on('change', function() {
				scope.$apply(function() {
					var val = ele[0].files[0];
					scope.setFileData({ value: val });
				});
			});
		}
	}
})
  1. In module with $httpProvider add dependency like ( Accept, Content-Type etc) with multipart/form-data. (Suggestion would be, accept response in json format) For e.g:

$httpProvider.defaults.headers.post['Accept'] = 'application/json, text/javascript'; $httpProvider.defaults.headers.post['Content-Type'] = 'multipart/form-data; charset=utf-8';

  1. Then create separate function in controller to handle form submit call. like for e.g below code:

  2. In service function handle "responseType" param purposely so that server should not throw "byteerror".

  3. transformRequest, to modify request format with attached identity.

  4. withCredentials : false, for HTTP authentication information.

in controller:

  // code this accordingly, so that your file object 
  // will be picked up in service call below.
  fileUpload.uploadFileToUrl(file); 


in service:

  .service('fileUpload', ['$http', 'ajaxService',
    function($http, ajaxService) {

      this.uploadFileToUrl = function(data) {
        var data = {}; //file object 

        var fd = new FormData();
        fd.append('file', data.file);

        $http.post("endpoint server path to whom sending file", fd, {
            withCredentials: false,
            headers: {
              'Content-Type': undefined
            },
            transformRequest: angular.identity,
            params: {
              fd
            },
            responseType: "arraybuffer"
          })
          .then(function(response) {
            var data = response.data;
            var status = response.status;
            console.log(data);

            if (status == 200 || status == 202) //do whatever in success
            else // handle error in  else if needed 
          })
          .catch(function(error) {
            console.log(error.status);

            // handle else calls
          });
      }
    }
  }])
<script src="//unpkg.com/angular/angular.js"></script>

This is pretty must just a copy of that projects demo page and shows uploading a single file on form submit with upload progress.

(function (angular) {
'use strict';

angular.module('uploadModule', [])
    .controller('uploadCtrl', [
        '$scope',
        '$upload',
        function ($scope, $upload) {
            $scope.model = {};
            $scope.selectedFile = [];
            $scope.uploadProgress = 0;

            $scope.uploadFile = function () {
                var file = $scope.selectedFile[0];
                $scope.upload = $upload.upload({
                    url: 'api/upload',
                    method: 'POST',
                    data: angular.toJson($scope.model),
                    file: file
                }).progress(function (evt) {
                    $scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
                }).success(function (data) {
                    //do something
                });
            };

            $scope.onFileSelect = function ($files) {
                $scope.uploadProgress = 0;
                $scope.selectedFile = $files;
            };
        }
    ])
    .directive('progressBar', [
        function () {
            return {
                link: function ($scope, el, attrs) {
                    $scope.$watch(attrs.progressBar, function (newValue) {
                        el.css('width', newValue.toString() + '%');
                    });
                }
            };
        }
    ]);
 }(angular));

HTML

<form ng-submit="uploadFile()">
   <div class="row">
         <div class="col-md-12">
                  <input type="text" ng-model="model.fileDescription" />
                  <input type="number" ng-model="model.rating" />
                  <input type="checkbox" ng-model="model.isAGoodFile" />
                  <input type="file" ng-file-select="onFileSelect($files)">
                  <div class="progress" style="margin-top: 20px;">
                    <div class="progress-bar" progress-bar="uploadProgress" role="progressbar">
                      <span ng-bind="uploadProgress"></span>
                      <span>%</span>
                    </div>
                  </div>

                  <button button type="submit" class="btn btn-default btn-lg">
                    <i class="fa fa-cloud-upload"></i>
                    &nbsp;
                    <span>Upload File</span>
                  </button>
                </div>
              </div>
            </form>

EDIT: Added passing a model up to the server in the file post.

The form data in the input elements would be sent in the data property of the post and be available as normal form values.

It is more efficient to send the files 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:

Doing Multiple $http.post Requests Directly from a FileList

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

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.


Working Demo of "select-ng-files" Directive that Works with ng-model1

The <input type=file> element does not by default work with the ng-model directive. It needs a custom directive:

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="fileList" multiple>
    
    <h2>Files</h2>
    <div ng-repeat="file in fileList">
      {{file.name}}
    </div>
  </body>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!