• 基于AngularJS+Bootstrap的多文件上传与管理


    一、前言

    最近一个项目中需要实现多文件上传与管理,而项目是基于bootstrap开发的,所以查了一些bootstrap文件上传插件,最后发现还是bootstrap-fileinput最美观,该插件可以实现多文件的上传与管理(插件官方地址:http://plugins.krajee.com/file-input),具体的效果如下:

     

    (bootstrap-fileinput不局限于图片上传,也可以实现文件上传,但图片的缩略图容易辨识,这里就以图片上传为例)

     该插件基本的操作可以参考:http://www.jq22.com/jquery-info5231,本文主要针对多文件管理

    在讲该插件如何使用前,先跟大家讲一下项目中关于图片管理的需求:

    1、  可以上传多个图片

    2、  只有当点击保存按钮时,图片信息才保存至数据库

    3、  可以加载已经保存到数据库的图片信息,并提供删除功能

    因此,我们可以规定几个文件状态:

    已选择:已经放入到插件中,但还没有上传到服务器。如上图中第3个图片,该图片下方有上传按钮。

    已上传:已经上传到服务器,但图片信息没有保存到数据库。如上图中第2个图片,该图片下方有100%的进度条。

    已保存:图片信息已经保存至数据库的图片,如上图中第1张图,这些图片下方有删除按钮,点击删除时会将图片信息从数据库中删除。

    二、引入必要文件

    <link href="<%=path%>/static/css/bootstrap-3.3.5/bootstrap.min.css" rel="stylesheet">
    <link href="<%=path%>/static/css/bootstrap-3.3.5/fileinput.css" rel="stylesheet">
    
    <script src="<%=path%>/static/js/jquery-1.11.3.js"></script>
    <script src="<%=path%>/static/js/angularjs-1.3.9/angular.min.js"></script>
    <script src="<%=path%>/static/js/bootstrap-3.3.5/bootstrap.min.js"></script>
    <script src="<%=path%>/static/js/bootstrap-3.3.5/fileinput.js"></script>
    <script src="<%=path%>/static/js/bootstrap-3.3.5/fileinput_locale_zh.js"></script>

      其中fileinput.js和fileinput_locale_zh.js都在插件待官方包中,angular.min.js和bootstrap.min.js就不多介绍了

    三、多文件上传

       首先在页面中定义file控件:

    <input id="input-images" type="file" multiple class="file-loading" accept="image/*">
    

      然后对该控件进行初始化,就可以实现该组件的多文件上传了:

    	    	$("#input-images").fileinput({
    	            uploadUrl: "<%=path%>" + "/album/pictureFileUpload",
    	            allowedFileExtensions: ["jpg", "png", "gif"],
    	            resizePreference: 'height',
    	            maxFileCount: 10,
    	            language: 'zh',
    	            overwriteInitial: false,
    	            resizeImage: true,
    	        });
    

      当然,初始化时的属性有很多,这里不一一介绍了,后台代码(使用JFinal)如下:

    	public void pictureFileUpload() {
    		UploadFile uploadFile = getFile();
    		renderJson("{"link":" + ""/fileinput/upload/" + uploadFile.getFileName()
    				+ """ + ","fileName":"" + uploadFile.getOriginalFileName()
    				+ ""}");
    	}
    

      注意最后一定要返回Json,哪怕返回一个空json串(“{}”),返回的值保存在前台的data.response中。

    四、已有文件的加载与删除

       已有文件的加载是指将服务器上已经存在的文件展示在该控件中,以实现文件管理,提供删除功能,这主要依赖于initialPreview实现的。

      将服务器上的文件名称和文件地址获取之后,使用initialPreview和initialPreviewConfig完成加载和定义删除操作:

                  var initPreview = new Array();//展示元素
    	    	     var initPreviewConfig = new Array();//展示设置
    	    	
    			$.post(
    				"<%=path%>" + "/album/getPicsByAlbum", 
    				{albumId : albumId}, 
    				function(result) {
    				for(var i=0;i<result.length;i++){
    				  var pictureFile = result[i];
    				    //用于展示已经上传的图片
    			            initPreview.push("<img src='" + pictureFile.PICADDRESS 
    			            		+ "' class='file-preview-image' alt='"+pictureFile.PICNAME+"' title='"+pictureFile.PICNAME+"'>");
    			            var config = new Object();
    			            config.caption = pictureFile.PICNAME;
    			            config.url="<%=path%>" + "/album/deletePicById";
    			            config.key=pictureFile.ID;
    			            initPreviewConfig.push(config);
    				}
    				initFileInput($scope);
    			        $("#input-images").fileinput('refresh', {
    			        	initialPreview: initPreview,
    			        	initialPreviewConfig: initPreviewConfig
    			        });
    			     }
    			);
    

      点击删除图标,会默认把config中待key值传至后台,后台中定义deletePicById方法即可:

    	public void deletePicById() {
    		String picId = getPara("key");
    		service.deletePicById(Integer.valueOf(picId));
    		renderJson("{}");
    	}

    五、几点疑问的解答

    1、为什么model里没有属性,却可以在前端展示相关属性?

        这里主要使用了JFinal的ActiveRecord功能,无需定义属性和setter,getter方法,属性值被映射在model里的attrs里,这个属性是<key, value>的键值对,而key值就是数据库的字段名。特别提醒:虽然SQL语句不分区大小写,但字段名还是存在大小写的,如果字段名是大写的,那么映射到model里的key就是大写的,同时JFinal的默认id为主键的策略也不能生效,需要在configPlugin中设置,如下:arp.addMapping("pictures", "ID", Picture.class),建议大家按照Java命名规范命名数据库字段。
    files.sql

    2、(参考代码)中初始化FileInput为什么要执行clear,destory操作?

        因为FileInput插件在选择文件后,不管有没有上传,都会保留文件在file域中,因此再点击时会显示上一次选择的文件,不符合多相册管理的需求,原本以为clear操作就可以清空file域(官方文档这么说的),但实际操作发现并没有清空,因此才调用clear,destory后再重新初始化文件上传控件。(这一点不太确定,希望有大神可以指点)

    3、保存时怎么知道那些图片需要存数据库,这是基于什么实现的?

    $scope上有个selectedPics数组,该属性负责保存最终那些文件会保存到数据库。在文件选择后会将选择待文件信息保存到这个数组中,但hasUpload属性为false;在文件上传后,会修改对应的hasUpload为true;在上传成功后执行删除(还没有保存到数据库)会从数组中移除对应的元素。有人会问,那选择文件后不上传直接删除,那文件信息岂不是会占用数据位置从而导致元素错乱吗?其实并不会,在fileuploaded事件中,哪些图片已经hasUpload,是直接改数组对应位置元素的值的,而数组坐标是通过图片所在DIV的data-fileindex属性值获得的,该值会一直增加,不会替补空缺值,不会因为删除图片而变动,正好与selectedPics数组相对应。
    var idx = $("#"+previewId).attr("data-fileindex");
    例如我选择了3张图片,此时没有上传,他们依次的data-fileindex为0,1,2,当我删除中间那个图片并重新选择新图片时,那么他们的data-fileindex就会变为0,2,3。

    六、代码参考

    文章写得不好,太粗略了,我给大家整理了下源码,单独打个包,直接用eclipse导入即可实现,数据库脚本为files.sql(MySQL),大家多看看代码吧。

    最后本人才学AngularJS,代码写的不够纯熟,如有不妥之处,欢迎大家留言。

    参考代码下载地址:https://files.cnblogs.com/files/kevin19900306/fileinput.zip

  • 相关阅读:
    有趣的放大镜
    特效代码
    向数据库添加学生信息。存放在REQUEST对象里
    机房servlet过滤器
    冒泡排序法
    验证码 随机生成器 详解
    生成器 种子
    生日
    在字符串里寻找某字符出现的个数
    课堂随笔
  • 原文地址:https://www.cnblogs.com/kevin19900306/p/5459859.html
Copyright © 2020-2023  润新知