AngularJS:如何实现具有多部分形式的简单文件上传?


144

我想从AngularJS到node.js服务器做一个简单的多部分表单发布,表单的一部分应包含JSON对象,另一部分应包含图像(我目前仅使用$ resource发布JSON对象)

我以为我应该从输入type =“ file”开始,但是后来发现AngularJS无法绑定到该文件。

我可以找到的所有示例都是用于包装jQuery插件以进行拖放的。我想要一个文件的简单上传。

我是AngularJS的新手,对自己编写指令完全不满意。


1
我认为这可能会有所帮助:noypi-linux.blogspot.com/2013/04/…–
Noypi Gilas

1
请参阅以下答案: stackoverflow.com/questions/18571001/… 那里已经有适用于系统的很多选择。
Anoyz 2014年

Answers:


188

一个真正有效的解决方案,除了angularjs之外没有其他依赖项(已通过v.1.0.6测试)

html

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>

Angularjs(1.0.6)在“ input-file”标签上不支持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!... );

};

最酷的部分是未定义的内容类型和transformRequest:angular.identity,它们使$ http能够选择正确的“内容类型”并管理处理多部分数据时所需的边界。


2
@Fabio你能解释一下这个transformRequest是做什么的吗?什么是angular.identity?我整天只是为了完成多部分文件上传而努力。
2014年

1
Java REST应用程序中的@RandomUser
Fabio Bonfante

2
哇,真是太好了,非常感谢。在这里,我还必须上传多张图片和其他一些数据,因此我将fd操作为fd.append("file", files[0]); fd.append("file",files[1]); fd.append("name", "MyName")
Moshii 2014年

1
console.log(fd)显示表格为空?这样吗
J Bourne

4
请注意,这是不行的,如果你有debuginfo软禁用(推荐)
布鲁诺·佩雷斯

42

您可以使用simple / lightweight ng-file-upload指令。对于具有FileAPI flash填充程序的非HTML5浏览器,它支持拖放,文件进度和文件上传

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

}];

这不是一次对每个文件执行单个POST请求吗?
Anoyz 2013年

是的,它确实。github问题跟踪器中有关于为什么最好一一上传文件的讨论。Flash API不支持一起发送文件,并且AFAIK Amazon S3也不支持发送文件。
danial 2013年

因此,您是在说一般更正确的方法是一次发送一个文件POST请求吗?我可以看到其中的几个优点,但是在提供服务器端静态支持时也会遇到更多麻烦。
Anoyz 2013年

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

1
这不是“简单/轻量级”。示例部分甚至没有上传仅一个文件的示例。
克里斯,克里斯(Chris

9

直接发送文件效率更高。

编码的base64Content-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);
};

发送带有File对象的POST时,进行设置很重要'Content-Type': undefined。然后,XHR send方法将检测File对象并自动设置内容类型。

要发送多个文件,请参阅进行多次$http.post从文件列表直接请求


我以为我应该从输入type =“ file”开始,但是后来发现AngularJS无法绑定到该文件。

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

适用于1的“ select-ng-files”指令的工作演示ng-model

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 内容类型 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>
$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);
};

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


filesInput指令似乎不适用于Angular 1.6.7,我可以看到中的文件,ng-repeat$scope.variable控制器中的文件为空白?也是您的示例使用的file-model一个,另一个是files-input
Dan

@丹我测试了它,它可以工作。如果您的代码有问题,则应使用最小,完整,可验证的示例询问新问题。将指令名称更改为select-ng-files。使用AngularJS 1.7.2测试。
georgeawg

5

我刚遇到这个问题。因此,有几种方法。首先是新的浏览器支持

var formData = new FormData();

通过此链接可以访问博客,其中包含有关如何将支持仅限于现代浏览器的信息,但否则,它可以完全解决此问题。

否则,您可以使用target属性将表单发布到iframe。发布表单时,请确保将目标设置为iframe,并将其display属性设置为none。目标是iframe的名称。(请您知道。)

我希望这有帮助


AFAIK FormData不适用于IE。也许对图像文件进行base64编码并将其发送为JSON是一个更好的主意?如何使用AngularJS绑定到输入type =“ file”以获取所选的文件路径?
Gal Ben-Haim

3

我知道这是一个较晚的条目,但是我创建了一个简单的上载指令。您可以立即开始工作!

<input type="file" multiple ng-simple-upload web-api-url="/api/post"
       callback-fn="myCallback" />

ng-simple-upload在Github 上提供更多示例,并提供使用Web API的示例。


2
老实说,建议在项目自述文件中复制粘贴代码可能是一个很大的污点。尝试将您的项目与常见的软件包管理器(例如npm或bower)集成。
Stefano Torresi

2

您可以通过$resource将数据分配给资源的params属性来通过进行上传,actions如下所示:

$scope.uploadFile = function(files) {
    var fdata = new FormData();
    fdata.append("file", files[0]);

    $resource('api/post/:id', { id: "@id" }, {
        postWithFile: {
            method: "POST",
            data: fdata,
            transformRequest: angular.identity,
            headers: { 'Content-Type': undefined }
        }
    }).postWithFile(fdata).$promise.then(function(response){
         //successful 
    },function(error){
        //error
    });
};

1

我只是为AngularJs中的一个简单的上传器编写了一个简单的指令(来自现有课程)。

(确切的jQuery上传器插件是https://github.com/blueimp/jQuery-File-Upload

使用AngularJs的简单上载器(带有CORS实现)

(尽管服务器端用于PHP,但是您也可以简单地更改它的节点)


1
不要忘记提及您没有时间关闭\响应您回购中的问题
Oleg Belousov

@OlegTikhonov:是的,认真地坚持了其他一些项目。很难集中精神。
sk8terboi87
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.