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






4.86/5 (10投票s)
本文档描述了使用 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>
- ng-app="AgApp" 我们引导一个 Angular 应用程序。
-
ng-controller="ImageUploadMultipleCtrl" 指定此页面的控制器,其中包含显示选定文件信息和文件上传功能的代码。
-
<input type="file" id="file" name="file" multiple onchange="angular.element(this).scope().setFile(this)"> 使我们能够输入多个文件。在 onchange 事件中,我们调用 setFile 函数,这是一个 Angular 函数。此函数收集你选择的所有文件并将它们放入一个名为 fileList 的 $scope 变量中。
-
一旦文件信息保存在 fileList 中。我们通过 <tr ng-repeat="file in fileList"> 循环遍历 fileList 来显示文件信息。
-
每个文件都有 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.js 和 imageUploadMultipleController.js。
第二步:使用 XMLHttpRequest 开发上传功能
我们将使用 XMLHttpRequest 开发我们的上传功能。XMLHttpRequest 是一个 API,它提供客户端功能,用于在客户端和服务器之间传输数据。它提供了一种无需完全刷新页面即可轻松从 URL 检索数据的方法。XMLHttpRequest 最初由 Microsoft 设计,并被 Mozilla、Apple 和 Google 采用。要获取有关 XMLHttpRequest 的更多信息,请参阅:
https://mdn.org.cn/en-US/docs/Web/API/XMLHttpRequest
我选择 XMLHttpRequest 是出于以下原因:
- 它为我们提供了异步上传多个文件的能力。
- 它提供了跟踪每个文件进度的途径。
让我们开始编写代码。完成第一步后,我们在名为 fileList 的数组中获得了所有文件。所以循环遍历 fileList,从列表中获取每个文件的名称、大小和类型,并将它们全部发送到另一个函数进行上传。所以,在 imageUploadMultipleController.js 中向我们的控制器添加两个函数:
- UploadFile() -- 点击上传按钮时调用此函数。
- 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 在上传的不同状态下会引发以下事件:
- progress:在上传过程中引发,并向我们发送关于进度状态的必要信息。
- load:文件上传并保存到目标后,并且服务器返回响应时引发。
- error:上传过程中发生任何错误时立即引发。
- 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 必须相同。
结语
我用不同类型的文件进行了测试。测试时,请尝试使用两个不同的计算机。我指的是尝试从网络计算机选择文件。这样您就可以找到进度计数的最佳结果。或者,您可以将项目上传到实时服务器,然后尝试从您的计算机上传文件。如果您的本地计算机运行正常,并且从同一台计算机选择文件,您可能看不到进度计数效果,因为本地计算机到本地计算机的上传速度非常快。