springMVC+bootstrap+angularJS(angular-file-upload控件)实现的文件上传

宦烈
2023-12-01

最近有个项目需求是上传文件,一般是压缩包。上传到服务器进行进一步的操作。且需要支持单、多文件上传
由于项目一直使用的是angularjs写的前端,所有学习研究了下它自己的上传插件,写了个demo。简单记录一下,留作笔记

angular-file-upload

插件的GitHub地址:点击跳转
api地址:点击跳转
官方案例:点击跳转

大部分逻辑都在代码注解中体现出来,不在累赘。直接贴代码

代码

1 . 未使用angular-file-upload控件

<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>上传任务测试</title>
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<link rel="stylesheet" href="../plugins/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="../plugins/adminLTE/css/AdminLTE.css">
<link rel="stylesheet" href="../plugins/adminLTE/css/skins/_all-skins.min.css">
<link rel="stylesheet" href="../css/style.css">
<script src="../plugins/jQuery/jquery-2.2.3.min.js"></script>
<script src="../plugins/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="../plugins/bootstrap/js/bootstrap-filestyle.js"></script>
<!-- 引入的文件上传样式 -->
<script type="text/javascript" src="../plugins/angularjs/angular.min.js"></script>
<script type="text/javascript" src="../plugins/angularjs/ui-bootstrap-tpls.min.js"></script>
<!-- 提示model文件 -->
<script type="text/javascript" src="../js/base.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
<script type="text/javascript" src="../js/controller/uploadController.js"></script>
<script type="text/javascript" src="../js/service/uploadService.js">
</script>
</head>
<body class="hold-transition skin-red sidebar-mini" ng-app="report" ng-controller="uploadController">
	<!-- 正文区域 -->
		<div class="box-body">
			<!--tab页-->
			<div class="nav-tabs-custom">
				<div class="tab-content">
					<div class="tab-pane active" id="home">
						<div class="modal-body">
							<!-- bootstrap开始 -->
							<div class="well">
								<h3>文件上传测试</h3>
								<hr>
								<div class="row">
									<div class="col-xs-6">
										<div class="form-group">
											<input type="file" name="file" id="file">
										</div>
									</div>
									<div class="col-xs-2">
										<div class="form-group">
											<button type="submit" class="btn btn-primary"
												ng-click="uploadfile()">
												<span class="glyphicon glyphicon-ok"></span> 上传
											</button>
										</div>
									</div>
								</div>
							</div>
							<!-- bootstrap结束-->
						</div>
					</div>
				</div>
			</div>
		</div>
</body>
<script type="text/javascript">
	//样式初始化
	$('#file').filestyle({
		'placeholder' : '请选择上传的源码压缩包(200M以内)'
	})
</script>
</html>

这是没使用上传插件编写的文件上传,核心是

<script type="text/javascript" src="../js/controller/uploadController.js"></script>
<script type="text/javascript" src="../js/service/uploadService.js">

这两个js文件中的方法
当点击上传按钮就会触发controller中的uploadfile方法,

$scope.uploadfile=function(){
		//新增
		uploadService.uploadFile().success(
				function(response){
					if(response.success){
						alert("上传成功!");
						$window.location.reload();//引入window服务,刷新当前页
					}else{
						alert("上传失败!");					
					}
				}		
			);
	}

然后去调用uploadservice中的上传方法

//上传文件
	this.uploadFile=function(){
		var formdata=new FormData();
		formdata.append('file',file.files[0]);     //file 文件上传框的name,就是根据这个来去页面获取文件
		return $http({
			url:'../upload.do',    //文件上传时候对应的controller
			method:'post',     
			data:formdata,    //上传的数据
			headers:{ 'Content-Type':undefined },   
			transformRequest: angular.identity			
		});
	}

参数解释:

  1. headers:{ ‘Content-Type’:undefined }:
    anjularjs使用http请求来上传文件,故需要使request成为一个Multipart/form-data请求,anjularjs对于post和get请求默认的Content-Type header 是application/json。通过设置‘Content-Type’: undefined,这样浏览器可以把Content-Type 设置为 multipart/form-data,还填充上当前的boundary,如果你手动设置为: ‘Content-Type’: multipart/form-data,后台会抛出异常:the current request boundary parameter is null。
  2. transformRequest: angular.identity
    通过设置 transformRequest: angular.identity ,anjularjs transformRequest function 将序列化我们的formdata object.

这样就会将前台选择的文件传递到后台进行处理,后台对应的Java方法如下,具体根据实际需求进行修改

@RestController
public class UploadController {
	@RequestMapping("/upload")
	public Result uploadCode(MultipartFile file) throws Exception {
		long startTime = System.currentTimeMillis();
		String originalFilename = file.getOriginalFilename();// 取文件的全名(带后缀的)
		System.out.println("文件名字"+originalFilename);
		Boolean remoteZipToFile = false;// 文件解压缩是否成功标识符
		try {
			// 重命名上传后的文件名(存储的文件名字)
			String fileName = file.getOriginalFilename();
			// 定义上传后的存储路径
			String path = "/Users/zshuai/Desktop/上传/" + fileName;
			File localFile = new File(path);
			// 如果这个文件目录不存在,先创建
			if (!localFile.exists()) {
				localFile.mkdirs();
			}
			file.transferTo(localFile);// 保存到代码运行的服务器上
			remoteZipToFile = true;
			long endTime = System.currentTimeMillis();
			System.out.println("采用file.Transto的运行时间:" + String.valueOf(endTime - startTime) + "ms");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new Result(remoteZipToFile, originalFilename);
	}

至于显示文件格式大小之类的,前端可以相应处理,本demo在后端springmvc.xml中设置了文件上传的大小,为800M

2. 使用angular-file-upload控件
页面

<!DOCTYPE html>
<html id="ng-app" ng-app="app"> <!-- id="ng-app" IE<8 -->

    <head>
        <title>文件上传控件</title>
		
		<link rel="stylesheet" href="../plugins/bootstrap/css/bootstrap.min.css">
        <!-- Fix for old browsers -->
        <script src="../js/es5-shim.min.js"></script>
        <script src="../js/es5-sham.min.js"></script>
        <script src="../plugins/jQuery/jquery-2.2.3.min.js"></script>
        <script src="../js/console-sham.js"></script>

		<script src="../plugins/bootstrap/js/bootstrap.min.js"></script>
        <!--<script src="../bower_components/angular/angular.js"></script>-->
        <script type="text/javascript" src="../plugins/angularjs/angular.min.js"></script>
        
        <script src="../js/angular-file-upload.min.js"></script>
        <script src="controllers.js"></script>

        <style>
            .my-drop-zone { border: dotted 3px lightgray; }
            .nv-file-over { border: dotted 3px red; } /* Default class applied to drop zones on over */
            .another-file-over-class { border: dotted 3px green; }
            html, body { height: 100%; }
        </style>

    </head>
    <!-- nv-file-drop:支持文件脱拽选择; nv-file-select:点击选择;uploader用于绑定在控制器中新建的upload对象。 -->
    <body ng-controller="AppController" nv-file-drop="" uploader="uploader" filters="queueLimit, customFilter">

        <div class="container">
            <div class="row">

                <div class="col-md-3">

                    <h3>选择文件</h3>
                    多文件
                    <input type="file" nv-file-select="" uploader="uploader" multiple  /><br/>

                    单文件
                    <input type="file" nv-file-select="" uploader="uploader" />
                </div>

                <div class="col-md-9" style="margin-bottom: 40px">

                    <h3>上传队列</h3>
                    <p>队列长度: {{ uploader.queue.length }}</p>

                    <table class="table">
                        <thead>
                            <tr>
                                <th width="50%">Name</th>
                                <th ng-show="uploader.isHTML5">尺寸</th>
                                <th ng-show="uploader.isHTML5">进展</th>
                                <th>状态</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr ng-repeat="item in uploader.queue">
                                <td><strong>{{ item.file.name }}</strong></td>
                                <td ng-show="uploader.isHTML5" nowrap>{{ item.file.size/1024/1024|number:2 }} MB</td>
                                <td ng-show="uploader.isHTML5">
                                    <div class="progress" style="margin-bottom: 0;">
                                        <div class="progress-bar" role="progressbar" ng-style="{ 'width': item.progress + '%' }"></div>
                                    </div>
                                </td>
                                <td class="text-center">
                                    <span ng-show="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span>
                                    <span ng-show="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span>
                                    <span ng-show="item.isError"><i class="glyphicon glyphicon-remove"></i></span>
                                </td>
                                <td nowrap>
                                    <button type="button" class="btn btn-success btn-xs" ng-click="item.upload()" ng-disabled="item.isReady || item.isUploading || item.isSuccess">
                                        <span class="glyphicon glyphicon-upload"></span> 上传
                                    </button>
                                    <button type="button" class="btn btn-warning btn-xs" ng-click="item.cancel()" ng-disabled="!item.isUploading">
                                        <span class="glyphicon glyphicon-ban-circle"></span> 取消
                                    </button>
                                    <button type="button" class="btn btn-danger btn-xs" ng-click="item.remove()">
                                        <span class="glyphicon glyphicon-trash"></span> 移除
                                    </button>
                                </td>
                            </tr>
                        </tbody>
                    </table>

                    <div>
                        <div>
                            队列进度:
                            <div class="progress" style="">
                                <div class="progress-bar" role="progressbar" ng-style="{ 'width': uploader.progress + '%' }"></div>
                            </div>
                        </div>
                        <button type="button" class="btn btn-success btn-s" ng-click="uploader.uploadAll()" ng-disabled="!uploader.getNotUploadedItems().length">
                            <span class="glyphicon glyphicon-upload"></span> 上传全部
                        </button>
                        <button type="button" class="btn btn-warning btn-s" ng-click="uploader.cancelAll()" ng-disabled="!uploader.isUploading">
                            <span class="glyphicon glyphicon-ban-circle"></span> 全部取消
                        </button>
                        <button type="button" class="btn btn-danger btn-s" ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
                            <span class="glyphicon glyphicon-trash"></span> 移除所有
                        </button>
                    </div>

                </div>

            </div>

        </div>

    </body>
</html>

注意看里面的注解

  <!-- nv-file-drop:支持文件脱拽选择; nv-file-select:点击选择;uploader用于绑定在控制器中新建的upload对象。 -->

此案例是依据官网提供的demo,再理解使用的时候加上自己的注释然后调用自己后台的方法实现
官网demo将前端controller以及service都写在一起,就没再变动,直接在里面进行修改使用

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

app.controller( 'AppController', [ '$scope', 'FileUploader', function($scope, FileUploader) {
					//注册过滤器
					var uploader = $scope.uploader = new FileUploader({
						url : '../upload.do' // 上传路径
					});
					// FILTERS
					// a sync filter
					//过滤器
					uploader.filters .push({
								name : 'syncFilter',
								fn : function(item /* {File|FileLikeObject} */,
										options) {
									console.log('syncFilter');
									return this.queue.length < 10;
								}
							});
					// an async filter
					uploader.filters.push({
						name : 'asyncFilter',
						fn : function(item /* {File|FileLikeObject} */, options,
								deferred) {
							console.log('asyncFilter');
							setTimeout(deferred.resolve, 1e3);
						}
					});

					// 回调函数

					//添加文件失败时
					uploader.onWhenAddingFileFailed = function( item /* {File|FileLikeObject} */, filter, options) {
						console.info('onWhenAddingFileFailed', item, filter,
								options);
					};
					//将单个文件添加到队列后触发
					uploader.onAfterAddingFile = function(fileItem) {
						console.info('onAfterAddingFile', fileItem);
					};
					//将所有拖动或选定的文件添加到队列后触发
					uploader.onAfterAddingAll = function(addedFileItems) {
						console.info('onAfterAddingAll', addedFileItems);
					};
					//在上传文件之前触发
					uploader.onBeforeUploadItem = function(item) {
						console.info('onBeforeUploadItem', item);
					};
					//文件上传进度
					uploader.onProgressItem = function(fileItem, progress) {
						console.info('onProgressItem', fileItem, progress);
					};
					//上传队列进度
					uploader.onProgressAll = function(progress) {
						console.info('onProgressAll', progress);
					};
					//已上传成功的文件
					uploader.onSuccessItem = function(fileItem, response,
							status, headers) {
						console.info('onSuccessItem', fileItem, response,
								status, headers);
					};
					//上传错误
					uploader.onErrorItem = function(fileItem, response, status,
							headers) {
						console.info('onErrorItem', fileItem, response, status, headers);
						alert("文件体积超过单个文件最大值800M(该数值对应后台配置文件中的springmvc.xml中的配置)")
					};
					//取消上传
					uploader.onCancelItem = function(fileItem, response,
							status, headers) {
						console.info('onCancelItem', fileItem, response,
								status, headers);
					};
					//文件上传完成(独立于操作的成功)
					uploader.onCompleteItem = function(fileItem, response,
							status, headers) {
						console.info('onCompleteItem', fileItem, response,
								status, headers);
					};
					//在上传整个队列时加载所有内容,或在上传单个独立文件时加载文件
					uploader.onCompleteAll = function() {
						console.info('onCompleteAll');
					};

					console.info('uploader', uploader);
				} ]);

里面添加了很多自己的个人理解注释,都是通过解读API理解后进行标注,若有不对的地方请留言告知,感谢
这样就简单实现了单文件、多文件的上传,且支持进度显示等操作,当然可以根据自己的实际需求进行二次开发。

源码包

补充:
上传文件时候涉及附带参数,以便区分(比如区分那个用户上传的)
一种是直接在上传路径URL中写死,这种静态的,如果参数是变化的,动态的,就不行了,可以用如下格式

// 注册量化分析工具过滤器
		var uploader = $scope.uploader = new FileUploader({
			url : '../upload.do' // 上传路径
		});
		//在上传文件之前触发,添加检测任务ID以及检测工具tag用来区分
		uploader.onBeforeUploadItem = function(item) {
			formData = [{"id":ids},{"tag":"fx"}];
			Array.prototype.push.apply(item.formData, formData);
		};

其中的参数可根据自己的需求进行动态设置绑定,因为onBeforeUploadItem此函数为将文件上传前触发,从时间逻辑上完全可以实现。后端接收直接根据参数key来获取即可

@RequestMapping("/upload")
	//id: 代表检测任务的ID  tag:代表那个工具发Q起的请求
	public Result uploadCode(MultipartFile file,String id, String tag) throws Exception {
		
		
        
	System.out.println("tid为:"+id);
		if (tag.equals("fx")) {
			System.out.println("分析工具");
			
		}
		
 类似资料: