使用AngularJS上传多个文件时的奇怪行为

时间:2014-10-23 10:38:27

标签: c# asp.net-mvc angularjs

我有一个使用AngularJS的ASP MVC 4项目。这是一个包含数据和任意数量文件的表单,需要将其发布到服务器。当我尝试发送多个文件时,ASP MVC无法将文件绑定到我的对象。发送单个文件时,表单和文件都会正确绑定到我的对象。

//multiple files action
public ActionResult Send(myModel model, IEnumerable<HttpPostedFileBase> files)
{
     //"model" contains the correct data
     //"files" is initialized but has Count = 0
     return View();
}

//single file action
public ActionResult SendSingle(myModel model, HttpPostedFileBase files)
{
     //"model" contains the correct data
     //"files" contains the selected file
     return View();
}

FormData对象由我的AngularJS控制器创建:

//AngularJS controller
var fData = new FormData();
fData.append("Prop1", "value1");
fData.append("Prop2", "value2");

var fileInputs = getFormFileInputs();
for (var i = 0; i < fileInputs.length; i++) {
    fData.append("files", fileInputs[i].files[0]);
}

MyService.sendData(fData);

服务(显然是?)设置标头并发送数据(相关代码):

sendData = function(formData) {
    $q.all({
        //ResponseData: $http.post('MyController/SendSingle', formData, { 
        ResponseData: $http.post('MyController/Send', formData, { 
           transformRequest: angular.identity,
           headers: { 'Content-Type': undefined }
        })
    }).then(...).catch(...);
}

真正奇怪的是files IEnumerable已实例化但不包含任何文件(Count() = 0)。检查服务器上的Request对象,我可以在Request.Files属性中看到所有上传的文件。 ModelState files参数附加了一个错误,该错误是由于尝试将字符串转换为HttpPostedFileBase时的异常引起的。

为什么服务器不会自动绑定files IEnumerable,即为什么服务器不会自动绑定二进制内容?

修改
这个接缝是一个反复出现的话题,所以我在这里添加这些信息。我已尝试将内容设置为multipart/form-data,因为这是&#34;标准&#34;在ASP MVC中发布数据和文件的方法。然而,这根本不起作用:表单数据和文件都不会自动绑定。我的模型属性都是null,文件对象也是null(而不是初始化但没有元素)。它接缝AngularJS对此内容类型标头有一些问题。另一方面,jQuery也是如此,您需要设置contentType: false, processData: false以发布表单数据和二进制内容。

3 个答案:

答案 0 :(得分:1)

我试图重现你的问题,但它适用于我,所以我只会发布我的代码。我希望它有所帮助:

HTML:

<div class="container" ng-controller="MainCtrl">
    <div class="col-md-6 col-md-offset-3">
        <p>Hello {{name}}!</p>
        <form>
            <fileupload></fileupload>
        </form>
    </div>
</div>

@section scripts
{
    <script type="text/javascript">

        var app = angular.module('app', []);

        app.controller('MainCtrl', function($scope) {
            $scope.name = 'World';
        });

        app.directive("fileupload", function($http) {
            return {
                replace: true,
                template: '<div><input id="file" type="file" name="img" multiple /> <button type="button" ng-click="upload()">Upload</button></div>',
                controller: function($scope) {
                    $scope.upload = function() {
                        var data = new FormData();

                        data.append("Value", "value1");

                        for (i = 0; i < $scope.files.length; i++) {
                            data.append('files', $scope.files[i]);
                        }

                        $http.post('Home/Upload', data, {
                            transformRequest: angular.identity,
                            headers: { 'Content-Type': undefined }
                        }).success(function(response) {
                            console.log(response);
                        });;

                        console.log('upload', $scope.files);
                    };
                },
                link: function(scope, element, attrs) {
                    element.find("#file").bind("change", function(changeEvent) {
                        scope.$apply(function() {
                            scope.files = changeEvent.target.files;
                            //console.log('files', changeEvent.target.files);
                        });
                    });
                }
            };
        });

    </script>
}

控制器:

public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Upload(TestModel model, IEnumerable<HttpPostedFileBase> files)
    {
        return Json(new { files = files.ToList().Select(x => x.FileName), model = model });
    }       
}

答案 1 :(得分:0)

您似乎未设置内容类型,请尝试将其设置为multipart/form-data。这应该指示服务器如何解析发布的内容。

答案 2 :(得分:-1)

如果您使用常规控制器,您可以尝试使用以下内容:

    foreach (string key in Request.Files)
    {
        HttpPostedFileBase file = Request.Files[key];
    }

如果您正在使用WebAPI:

if (Request.Content.IsMimeMultipartContent())
            {
                var rootPath = TMP_SAVE_PATH; //some path
                var provider = new MultipartFormDataStreamProvider(rootPath);

                await Request.Content.ReadAsMultipartAsync(provider);

                foreach (var fileData in provider.FileData)
                {
                    var fileName = fileData.Headers.ContentDisposition.FileName.TrimEnd(new char[] { '\\' }).Trim(new char[] { '"' });
                    File.Move(fileData.LocalFileName, fileName); //move to some location
                }
            }