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

Posted

技术标签:

【中文标题】AngularJS:如何使用多部分表单实现简单的文件上传?【英文标题】:AngularJS: how to implement a simple file upload with multipart form? 【发布时间】:2012-12-07 10:23:40 【问题描述】:

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

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

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

我是 AngularJS 的新手,对编写自己的指令感到不自在。

【问题讨论】:

我认为这可能会有所帮助:noypi-linux.blogspot.com/2013/04/… 查看这个答案:***.com/questions/18571001/… 已经有很多可供选择的系统。 See here 【参考方案1】:

一个真正可行的解决方案,除了 angularjs 之外没有其他依赖项(使用 v.1.0.6 测试)

html

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

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

;

很酷的部分是 undefined 内容类型和 transformRequest: angular.identity 让 $http 能够选择正确的“内容类型”并管理处理多部分数据时所需的边界。

【讨论】:

@Fabio 你能解释一下这个 transformRequest 是做什么的吗? angular.identity 是什么?我整天都在努力完成分段文件上传。 @RandomUser 在 java rest 应用程序中,类似于 mkyong.com/webservices/jax-rs/file-upload-example-in-jersey 哇,太棒了,非常感谢。在这里,我还必须上传多张图片和一些其他数据,所以我将 fd 操作为fd.append("file", files[0]); fd.append("file",files[1]); fd.append("name", "MyName") console.log(fd) 显示表单为空?是这样吗? 请注意,如果您禁用了debugInfo(建议),这将不起作用【参考方案2】:

您可以使用简单/轻量级的ng-file-upload 指令。 它通过 FileAPI flash shim 支持非 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 请求吗? 是的。 github问题跟踪器中有关于为什么最好逐个上传文件的讨论。 Flash API 不支持一起发送文件,AFAIK Amazon S3 也不支持。 那么,您是说一般更正确的方法是一次发送一个文件 POST 请求?我可以看到其中的几个优点,但在制作服务器端的 RESTful 支持时也比较麻烦。 我实现它的方式是将每个文件作为资源保存上传,服务器会将其保存在本地文件系统(或数据库)上并返回一个唯一的ID(即随机文件夹/文件名或db id) 用于该文件。然后,一旦所有上传完成,客户端就会发送另一个 PUT/POST 请求,其中包含为此请求上传的文件的额外数据和 ID。然后服务器将记录与相关文件一起保存。当您上传文件然后发送电子邮件时,它就像 gmail。 这不是“简单/轻量级”。示例部分甚至没有仅上传一个文件的示例。【参考方案3】:

直接发送文件效率更高。

Content-Type: multipart/form-data 的 base64 encoding 增加了 33% 的额外开销。如果服务器支持,直接发送文件效率更高:

$scope.upload = function(url, file) 
    var config =  headers:  'Content-Type': undefined ,
                   transformResponse: angular.identity
                 ;
    return $http.post(url, file, config);
;

发送带有File object 的POST 时,设置'Content-Type': undefined 很重要。然后XHR send method 会检测到File object 并自动设置内容类型。

要发送多个文件,请参阅Doing Multiple $http.post Requests Directly from a FileList


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

&lt;input type=file&gt; 元素默认情况下不适用于ng-model directive。它需要一个custom directive:

ng-model一起使用的“select-ng-files”指令的工作演示1

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 method 将检测FormData 对象并自动将内容类型标头设置为multipart/form-data 和proper boundary。

【讨论】:

filesInput 指令似乎不适用于 Angular 1.6.7,我可以看到 ng-repeat 中的文件,但相同的 $scope.variable 在控制器中是空白的?您的示例之一还使用file-model 和一个files-input @Dan 我测试了它并且它有效。如果您的代码有问题,您应该使用Minimal, Complete, Verifiable example 提出一个新问题。将指令名称更改为 select-ng-files。使用 AngularJS 1.7.2 测试。【参考方案4】:

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

var formData = new FormData();

Follow this link to a blog with info about 支持仅限于现代浏览器,否则它完全解决了这个问题。

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

希望对你有帮助

【讨论】:

AFAIK FormData 不适用于 IE。也许对图像文件进行base64编码并以JSON格式发送是一个更好的主意?如何使用 AngularJS 绑定到输入 type="file" 以获取选择的文件路径?【参考方案5】:

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

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

ng-simple-upload Github 上的更多内容,其中包含使用 Web API 的示例。

【讨论】:

说实话,建议在项目自述文件中复制粘贴代码可能是一个很大的黑标。尝试将您的项目与常见的包管理器(如 npm 或 bower)集成。【参考方案6】:

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

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

【讨论】:

【参考方案7】:

我刚刚为 AngularJs 中的简单上传器编写了一个简单的指令(来自现有的当然)。

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

A Simple Uploader using AngularJs (with CORS Implementation)

(虽然服务器端是php的,你也可以简单地改变它的节点)

【讨论】:

别忘了提到你没有给任何时间来关闭\回应你的回购中的问题 @OlegTikhonov:是的,严重坚持了其他一些项目。几乎无法集中注意力。

以上是关于AngularJS:如何使用多部分表单实现简单的文件上传?的主要内容,如果未能解决你的问题,请参考以下文章

如何利用angularjs打造一款简单web应用

使用angularjs实现注册表单

如何使用angularjs添加动态过滤功能来处理高级搜索?

Angularjs - 简单的表单提交

如何使用来自 AngularJS 控制器的无效输入正确清理表单?

angularjs表单提交 怎么接受数据