65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 AngularJS 和 ASP.net MVC 进行多文件上传

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (10投票s)

2015年11月4日

CPOL

5分钟阅读

viewsIcon

58909

downloadIcon

2898

本文档描述了使用 AngularJS 和 ASP.net MVC 进行多文件上传的过程,并跟踪每个文件的上传进度。

源代码

引言

这几天我一直在尝试使用 Angular 和 ASP.net MVC 上传多张图片。我还想跟踪每个文件的上传进度。我谷歌了很多。找到很多代码,但有些不起作用。经过 6 天的努力,我终于找到了正确的方法。我没有找到一个地方能收集所有信息(可能我的搜索能力很差)。所以我的目标是将所有内容汇集到一个地方,希望能帮助你学习使用 angularJS 和 asp.net MVC 上传多文件的过程。让我们开始吧。

描述

我假设你了解 AngularJS 的使用方法。我也假设你了解 Twitter Bootstrap、Font Awesome、HTML5 和 CSS3。我将一步一步进行。

第一步:准备 HTML 文件并显示选定文件信息

我们使用 HTML input 标签来输入多文件/单文件上传。HTML 文件如下:

<html ng-app="AgApp">
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ImageUploadMultiple</title>
    <link href="~/App_Content/CSS/bootstrap.min.css" rel="stylesheet" />
    <link href="~/App_Content/CSS/font-awesome.min.css" rel="stylesheet" />
</head>
<body>
     <div class="col-md-12">
        <h2>Image/File Upload With Angular::</h2>
    </div>

    <div ng-controller="ImageUploadMultipleCtrl">

        <div class="col-md-12" style="text-align:center;margin-bottom:10px;">
            <input type="file" id="file" name="file" multiple onchange="angular.element(this).scope().setFile(this)" accept="image/*" class="btn btn-warning" /> 
        </div>
        <div class="col-md-12">
            <button ng-click="UploadFile()" class="btn btn-primary" >Upload File</button>
        </div>
        
        <div class="col-md-12" style="padding-top:10px;">

                <div class="col-md-7">
                    <table class="table table-bordered table-striped">
                        <thead>
                            <tr>
                                <th>File Name</th>
                                <th>File Type</th>
                                <th>File Size</th>
                                <th>Status</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr ng-repeat="file in fileList">

                                <td>{{file.file.name}}</td>
                                <td>{{file.file.type}}</td>
                                <td>{{file.file.size}}</td>
                                <td>
                                    <div id="{{'P'+$index}}">
                                        
                                    </div>
                                </td>

                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
     </div>
    <!-- Loading JQuery-->
    <script src="~/App_Content/JS/jquery-1.7.2.min.js"></script>
    <script src="~/App_Content/JS/jquery.unobtrusive-ajax.min.js"></script>
    <!-- Lodaing the Angular JS-->
    <script src="~/App_Content/JS/angular.min.js"></script>
    
    <!-- Load our Angular Application-->
    <script src="~/App_Content/JS/Practical/Module.js"></script>
    <!-- Load the page Controller-->
    <script src="~/App_Content/JS/Practical/ImageUploadMultipleController.js"></script>
</body>
</html>
  1.  ng-app="AgApp" 我们引导一个 Angular 应用程序。
  2. ng-controller="ImageUploadMultipleCtrl" 指定此页面的控制器,其中包含显示选定文件信息和文件上传功能的代码。

  3. <input type="file" id="file" name="file" multiple onchange="angular.element(this).scope().setFile(this)"> 使我们能够输入多个文件。在 onchange 事件中,我们调用 setFile 函数,这是一个 Angular 函数。此函数收集你选择的所有文件并将它们放入一个名为 fileList 的 $scope 变量中。

  4. 一旦文件信息保存在 fileList 中。我们通过 <tr ng-repeat="file in fileList"> 循环遍历 fileList 来显示文件信息。

  5. 每个文件都有 3 个自动属性:name、type 和 size。你可以直接访问文件的这 3 个属性。

现在让我们转到控制器,开发 setFile() 函数来设置 fileList。一旦我们用你选择的图片设置了 fileList,Angular 就会立即显示选定的文件信息。这就是 Angular 自动绑定的魔力。

首先,我们必须定义 Angular 模块。在 Module.js 文件中,我们这样做了。这是 Module.js 文件:

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

现在使用这个模块来开发我们的 Angular 文件上传控制器。以下代码是一个带有 SetFile() 函数的 Angular 控制器。ImageUploadMultipleController.js 文件如下:

app.controller('ImageUploadMultipleCtrl', function ($scope) {

    $scope.fileList = [];
    $scope.curFile;
    $scope.ImageProperty = {
        file:''
    }

    $scope.setFile = function (element) {
        $scope.fileList = [];
       // get the files
        var files = element.files;
        for(var i=0;i<files.length;i++)
        {
            $scope.ImageProperty.file = files[i];
            
            $scope.fileList.push($scope.ImageProperty);
            $scope.ImageProperty = {};
            $scope.$apply();
          
        }
    }

});

这里 ImageUploadMultipleCtrl 是我们的控制器名称。 $scope 变量 fileList 是你选择的文件数组。这结束了我们的第一步,运行项目并按住 Ctrl 键选择多个文件,你将看到以下效果。不要忘记在页面底部像上面 HTML 文件中所示那样添加 Module.jsimageUploadMultipleController.js

第二步:使用 XMLHttpRequest 开发上传功能

我们将使用 XMLHttpRequest 开发我们的上传功能。XMLHttpRequest 是一个 API,它提供客户端功能,用于在客户端和服务器之间传输数据。它提供了一种无需完全刷新页面即可轻松从 URL 检索数据的方法。XMLHttpRequest 最初由 Microsoft 设计,并被 Mozilla、Apple 和 Google 采用。要获取有关 XMLHttpRequest 的更多信息,请参阅:

https://mdn.org.cn/en-US/docs/Web/API/XMLHttpRequest

我选择 XMLHttpRequest 是出于以下原因:

  1. 它为我们提供了异步上传多个文件的能力。
  2. 它提供了跟踪每个文件进度的途径。

让我们开始编写代码。完成第一步后,我们在名为 fileList 的数组中获得了所有文件。所以循环遍历 fileList,从列表中获取每个文件的名称、大小和类型,并将它们全部发送到另一个函数进行上传。所以,在 imageUploadMultipleController.js 中向我们的控制器添加两个函数:

  1. UploadFile() -- 点击上传按钮时调用此函数。
  2. UploadFileIndividual(fileToUpload,name,type,size,index) -- 负责上传单个文件。它接受 4 个参数:要上传的文件、文件名、文件类型、文件大小和索引。
$scope.UploadFile=function()
    {

        for (var i = 0; i < $scope.fileList.length; i++)
        {

            $scope.UploadFileIndividual($scope.fileList[i].file,
                                        $scope.fileList[i].file.name,
                                        $scope.fileList[i].file.type,
                                        $scope.fileList[i].file.size,
                                        i);
        }

    }

 

$scope.UploadFileIndividual = function (fileToUpload,name,type,size,index)
    {
        //Create XMLHttpRequest Object
        var reqObj = new XMLHttpRequest();

       //open the object and set method of call(get/post), url to call, isAsynchronous(true/False)
        reqObj.open("POST", "/FileUpload/UploadFiles", true);

       //set Content-Type at request header.for file upload it's value must be multipart/form-data
        reqObj.setRequestHeader("Content-Type", "multipart/form-data");

        //Set Other header like file name,size and type
        reqObj.setRequestHeader('X-File-Name', name);
        reqObj.setRequestHeader('X-File-Type', type);
        reqObj.setRequestHeader('X-File-Size', size);

        // send the file
        reqObj.send(fileToUpload);

    }

就这样。注释已经足够描述代码了。此步骤的结果在开发 ASP.net MVC 控制器以接收 XMLHttpRequest 发送的对象之前是不可见的。

第三步:开发 ASP.net MVC 控制器

现在我们需要开发一个 ASP.net MVC 控制器来接收从 XMLHttpRequest 对象发送的文件。

        [HttpPost]
        public virtual string UploadFiles(object obj)
        {
            var length = Request.ContentLength;
            var bytes = new byte[length];
            Request.InputStream.Read(bytes, 0, length);

            var fileName = Request.Headers["X-File-Name"];
            var fileSize = Request.Headers["X-File-Size"];
            var fileType = Request.Headers["X-File-Type"];

            var saveToFileLoc = "\\\\adcyngctg\\HRMS\\Images\\" + fileName;

            // save the file.
            var fileStream = new FileStream(saveToFileLoc, FileMode.Create, FileAccess.ReadWrite);
            fileStream.Write(bytes, 0, length);
            fileStream.Close();

            return string.Format("{0} bytes uploaded", bytes.Length);
        }

现在,如果我们运行项目并在选择图片后单击上传文件按钮,我们会发现所有图片都已成功上传。但我们没有收到任何消息或进度。让我们继续下一步,在那里我们开发 XMLHttpRequest 对象的事件处理程序。

第四步:跟踪单个文件上传进度

XMLHttpRequest 对象会返回每个文件每个事件的状态。通过编写事件处理函数,我们可以访问返回的状态。我们可以使用 XMLHttpRequest 对象的 addEventListener 方法来处理事件。XMLHttpRequest 在上传的不同状态下会引发以下事件:

  1. progress:在上传过程中引发,并向我们发送关于进度状态的必要信息。
  2. load:文件上传并保存到目标后,并且服务器返回响应时引发。
  3. error:上传过程中发生任何错误时立即引发。
  4. abort:用户取消上传过程时立即引发。

为了处理进度,我们需要定义

/*
the following command catch the progress event and pass it to our own function named 
uploadProgress.
*/
reqObj.upload.addEventListener("progress", uploadProgress, false)

function uploadProgress(evt) {
            if (evt.lengthComputable) {
               
               var uploadProgressCount = Math.round(evt.loaded * 100 / evt.total);
           
               document.getElementById('P' + index).innerHTML = uploadProgressCount;

              // when upload complete then show a spiner icon till the server send back a response
               if(uploadProgressCount==100)
               {
                   document.getElementById('P' + index).innerHTML =

                  '<i class="fa fa-refresh fa-spin" style="color:maroon;"></i>';
               }
                
            }
        }

为了处理加载完成,我们需要定义

reqObj.addEventListener("load", uploadComplete, false)

function uploadComplete(evt) {
            /* This event is raised when the server  back a response */

            document.getElementById('P' + index).innerHTML = 'Saved';
            $scope.NoOfFileSaved++;
            $scope.$apply();
        }

 为了处理上传过程中的错误,我们需要定义

reqObj.addEventListener("error", uploadFailed, false)

function uploadFailed(evt) {
            document.getElementById('P' + index).innerHTML = 'Upload Failed..';
        }

为了处理上传取消,我们需要定义

reqObj.addEventListener("abort", uploadCanceled, false)

function uploadCanceled(evt) {

            document.getElementById('P' + index).innerHTML = 'Canceled....';
        }

就这样。我们的上传控制器现在已准备就绪。最终的控制器看起来像这样(imageUploadMultipleController.js):

$scope.UploadFileIndividual = function (fileToUpload,name,type,size,index)
    {
        //Create XMLHttpRequest Object
        var reqObj = new XMLHttpRequest();

        //event Handler
        reqObj.upload.addEventListener("progress", uploadProgress, false)
        reqObj.addEventListener("load", uploadComplete, false)
        reqObj.addEventListener("error", uploadFailed, false)
        reqObj.addEventListener("abort", uploadCanceled, false)


       //open the object and set method of call(get/post), url to call, isasynchronous(true/False)
        reqObj.open("POST", "/FileUpload/UploadFiles", true);

       //set Content-Type at request header.For file upload it's value must be multipart/form-data
        reqObj.setRequestHeader("Content-Type", "multipart/form-data");

        //Set Other header like file name,size and type
        reqObj.setRequestHeader('X-File-Name', name);
        reqObj.setRequestHeader('X-File-Type', type);
        reqObj.setRequestHeader('X-File-Size', size);


        // send the file
        reqObj.send(fileToUpload);

        function uploadProgress(evt) {
            if (evt.lengthComputable) {
               
               var uploadProgressCount = Math.round(evt.loaded * 100 / evt.total);
           
               document.getElementById('P' + index).innerHTML = uploadProgressCount;

               if(uploadProgressCount==100)
               {
                   document.getElementById('P' + index).innerHTML = 
                  '<i class="fa fa-refresh fa-spin" style="color:maroon;"></i>';
               }
                
            }
        }

        function uploadComplete(evt) {
            /* This event is raised when the server  back a response */

            document.getElementById('P' + index).innerHTML = 'Saved';
            $scope.NoOfFileSaved++;
            $scope.$apply();
        }

        function uploadFailed(evt) {
            document.getElementById('P' + index).innerHTML = 'Upload Failed..';
        }

        function uploadCanceled(evt) {

            document.getElementById('P' + index).innerHTML = 'Canceled....';
        }

    }

运行项目,您将看到以下图片。(请按第五步所示更改 web.config)

第五步:在 Web.config 中更改最大上传长度

为了上传大图片,您需要更改 Web.config 中的最大上传长度。

在 <system.web> 部分,按以下方式更改 http:

<httpRuntime targetFramework="4.5" maxRequestLength="1048576" />

按以下方式添加/更新 <system.webServer> 部分:

<system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
  </system.webServer>

请记住,<system.web> 中的 maxRequestLength 值和 <system.webserver> 中的 maxAllowedContentLength 必须相同。

结语

我用不同类型的文件进行了测试。测试时,请尝试使用两个不同的计算机。我指的是尝试从网络计算机选择文件。这样您就可以找到进度计数的最佳结果。或者,您可以将项目上传到实时服务器,然后尝试从您的计算机上传文件。如果您的本地计算机运行正常,并且从同一台计算机选择文件,您可能看不到进度计数效果,因为本地计算机到本地计算机的上传速度非常快。

© . All rights reserved.