AngularJS:如何使用multipart表单实现简单的文件上传?

Gal*_*aim 142 angularjs angularjs-fileupload

我想从AngularJS到node.js服务器做一个简单的多部分表单帖子,表单应该在一个部分包含一个JSON对象,在另一个部分包含一个图像,(我目前只发布带有$ resource的JSON对象)

我想我应该从input type ="file"开始,但后来发现AngularJS无法绑定到那个..

我能找到的所有例子都是用于拖放jQuery插件的拖放.我想要一个简单的上传一个文件.

我是AngularJS的新手,对编写我自己的指令感到不舒服.

Fab*_*nte 185

一个真正有效的解决方案,没有其他依赖关系而不是angularjs(用v.1.0.6测试)

HTML

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>
Run Code Online (Sandbox Code Playgroud)

Angularjs(1.0.6)不支持"输入文件"标签上的ng-model,因此您必须以"本地方式"执行此操作,以便从用户传递所有(最终)选定的文件.

调节器

$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!... );

};
Run Code Online (Sandbox Code Playgroud)

很酷的部分是未定义的内容类型和transformRequest:angular.identity,它们在$ http处为选择正确的"内容类型"并管理处理多部分数据时所需的边界.

  • 请注意,如果禁用[debugInfo](https://docs.angularjs.org/guide/production#disabling-debug-data)(如建议的话),这将不起作用 (4认同)
  • @Fabio你能解释一下这个transformRequest做了什么吗?angular.identity是什么?为了完成多部分文件上传,我当天正在敲打我的脑袋. (2认同)
  • 哇,太棒了,非常感谢.在这里我必须上传多个图像和一些其他数据,所以我操作fd为`fd.append("file",files [0]); fd.append( "文件",文件[1]); fd.append("name","MyName")` (2认同)

dan*_*ial 42

您可以使用简单/轻量级ng-file-upload指令.它支持使用FileAPI flash shim的非HTML5浏览器的拖放,文件进度和文件上传

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>
Run Code Online (Sandbox Code Playgroud)

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

}];
Run Code Online (Sandbox Code Playgroud)

  • 我实现它的方法是将每个文件作为资源保存上传,服务器将其保存在本地文件系统(或数据库)上,并返回该文件的唯一ID(即随机文件夹/文件名或db id).然后,一旦完成所有上传,客户端就会发送另一个PUT/POST请求,其中包含为此请求上传的文件的额外数据和ID.然后服务器将保存带有相关文件的记录.当您上传文件然后发送电子邮件时,就像gmail一样. (2认同)

geo*_*awg 7

直接发送文件更有效.

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);
};
Run Code Online (Sandbox Code Playgroud)

使用File对象发送POST时,设置很重要'Content-Type': undefined.然后,XHR send方法将检测File对象并自动设置内容类型.

要发送多个文件,请参阅直接从FileList 执行多个$http.post请求


我想我应该从input type ="file"开始,但后来发现AngularJS无法绑定到那个..

<input type=file>默认情况下,该元素不适用于ng-model指令.它需要一个自定义指令:

使用"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);
      })
    }
  }
});
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


$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>
Run Code Online (Sandbox Code Playgroud)
$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);
};
Run Code Online (Sandbox Code Playgroud)

使用FormData API发送POST时,设置很重要'Content-Type': undefined.然后,XHR发送方法将检测FormData对象并自动将内容类型标头设置为具有适当边界的multipart/form-data.


Edg*_*nez 5

我刚才有这个问题.所以有一些方法.首先是新的浏览器支持

var formData = new FormData();
Run Code Online (Sandbox Code Playgroud)

请点击此链接访问博客,其中包含有关如何将支持仅限于现代浏览器的信息,否则它将完全解决此问题.

否则,您可以使用target属性将表单发布到iframe.发布表单时,请务必将目标设置为iframe,并将其display属性设置为none.目标是iframe的名称.(就这样你知道.)

我希望这有帮助