关于JEECG数据导入的改进完善

本文详细阐述了解决大数据导入时客户端上传与后台处理分离的问题,通过将数据导入分为两步完成:首先使用uploadify进行文件上传并保存到服务器,然后发起异步请求将上传的文件进行导入操作。此方法有效解决了大数据导入时前端显示已完成而实际后台任务尚未完成的问题,确保了用户体验和数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在研究JEECG,遇到一个问题,就是数据导入。
官方版本提供的实例中的EXCEL 导入是用的uploadify插件,该插件提供了一系列的好用的方法接口,能够灵活的处理过程中的各个环节,但是有个问题是,uploadify只是负责客户端的上传监控,他检测着要是上传完毕了,就会去执行onUploadSuccess方法和onQueueComplete对应的方法。如果上传的文件小并且导入时间短的话,不会有什么问题,在前台执行这两个方法的时候后台能够返回执行结果,但是如果导入的文件比较大,就有问题了。
如果文件比较大,uploadify上传完成文件之后就会默认为执行完毕可以返回结果了,但是其实后面还要进行复杂的长时间的导入(插入数据库)操作,这样uploadify就不会等你后面的操作了,他会直接就去执行onUploadSuccess方法和onQueueComplete。这样导致的结果是,在前台看来导入完成了,导入界面关闭了,导入列表刷新了,但是其实后台还在执行着导入操作。这样对于用户来讲是很不友好的。用户在前台看到的是执行完毕,但是导入的数据跟自己excel中的数据不一致,但是又没有报错(当然,这种情况官网提供的实例tip提示也不会有任何内容),其实后台还没有执行完毕,并且如果导入出现问题也没法在前台进行展示了。
这个问题纠结了好几天,怎么解决呢!
想到了在工作中做过的数据导入(用的是JSF框架),原理是将数据的上传和数据的导入分开来操作,上传专门上传,导入专门导入。
问题解决设想:
导入分两步完成:
第一步:上传
用uploadify进行上传,上传后将文件保存到服务器的某个临时文件中,返回上传结果,结束本次请求
第二步:导入
上次请求结束后,紧接着利用AJAX的异步性,发起一个请求,该请求专门负责将上一步上传的文件 进行导入操作。这一步就会等到该请求真正返回结果后才会在前台,完成本次请求,关闭上传页面,刷新数据列表。
其实对于用户来讲还是一步完成。
到此为止就能完美的解决以上问题。
问题解决实现:
第一步:上传

上传页面的代码:personUpload.jsp

<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
    <%@include file="/context/mytags.jsp" %>
        <!DOCTYPE html>
        <html>
            
            <head>
                <title>
                    批量导入
                </title>
                <t:base type="jquery,easyui,tools">
                </t:base>
            </head>
            
            <body style="overflow-y: hidden" scroll="no">
                <t:formvalid formid="formobj" layout="div" dialog="true" beforeSubmit="upload"
                tiptype="1">
                    <fieldset class="step">
                        <div class="form">
                            <t:upload name="fiels" buttonText="选择要导入的文件" uploader="jformPersonInfoController.do?uploadPersonFile&id=${id}&importType=${importType}"
                            extend="*.xls;*.xlsx" id="file_upload" dialog="false" formData="documentTitle"
                            multi="false" callback="forbiddenPage(reJsonData)" view="true">
                            </t:upload>
                        </div>
                        <div class="form" id="filediv" style="height: 50px">
                        </div>
                    </fieldset>
                </t:formvalid>
                <script type="text/javascript" src="webpage/gwc/db/person/jformPersonInfo.js">
                </script>
            </body>
        
        </html>

注意:dialog设置为false,因为dialoag的默认值为true,必须明确的设定dialog为false,
那么dialog的作用是什么呢,如果dialog为true,就会执行刷新数据列表,关闭上传页面,并且弹出提示信息,并且callback函数用于没有机会执行。这些在我们没有真正执行完成,我们是不希望这样操作的。
设置callback回调函数,该函数是在执行onQueueComplete(可以看做是upladify最后执行的函数)的时候执行的,这样问题的思路就明确了,就是在第一步执行完成上传之后,不是进行刷新列表,关闭上传页面,弹出信息,而是执行回调函数
注:本人修改了官方的upload标签,增加了reJsonData公共变量, 将onUploadSuccess返回的json数据赋值给reJsonData,然后在回调函数进行传递:
修改UploadTag.java

public StringBuffer end() {
		StringBuffer sb = new StringBuffer();
		if("pic".equals(extend))
		{
			extend="*.jpg;*,jpeg;*.png;*.gif;*.bmp;*.ico;*.tif";
		}
		if(extend.equals("office"))
		{
			extend="*.doc;*.docx;*.txt;*.ppt;*.xls;*.xlsx;*.html;*.htm";
		}
		sb.append("<link rel=\"stylesheet\" href=\"plug-in/uploadify/css/uploadify.css\" type=\"text/css\"></link>");
		sb.append("<script type=\"text/javascript\" src=\"plug-in/uploadify/jquery.uploadify-3.1.js\"></script>");
		sb.append("<script type=\"text/javascript\" src=\"plug-in/tools/Map.js\"></script>");
		sb.append("\n<script type=\"text/javascript\">"
				+"var flag = false;"
				+"var fileitem=\"\";"
				+"var fileKey=\"\";"
				+"var serverMsg=\"\";"
				+"var m = new Map();"
				+"var reJsonData;"
				+ "$(function(){"
				+"$(\'#"+id+"\').uploadify({"
				+"buttonText:\'"+buttonText+"\',"
				//按钮的长度
				//+"width::\'"+150+"\',"
				+"auto:"+auto+","
				+"progressData:\'speed\'," 
				+"multi:"+multi+","
				+"height:25,"
				+"overrideEvents:[\'onDialogClose\']," 
				+"fileTypeDesc:\'文件格式:\'," 
				+"queueID:\'"+queueID+"\',"
				+"fileTypeExts:\'"+extend+"\',"
				+"fileSizeLimit:\'15MB\',"
				//增加文件限制
				+"queueSizeLimit:\'1\',"
				+"swf:\'plug-in/uploadify/uploadify.swf\',	"
				+"uploader:\'"+getUploader()			
						+"onUploadStart : function(file) { ");	
				if (formData!=null) {
					String[] paramnames=formData.split(",");				
					for (int i = 0; i < paramnames.length; i++) {
						String paramname=paramnames[i];
						sb.append("var "+paramname+"=$(\'#"+paramname+"\').val();");
					}				 
			        sb.append("$(\'#"+id+"\').uploadify(\"settings\", \"formData\", {");
			        for (int i = 0; i < paramnames.length; i++) {
						String paramname=paramnames[i];
						if (i==paramnames.length-1) {
							sb.append("'"+paramname+"':"+paramname+"");	
						}else{
							sb.append("'"+paramname+"':"+paramname+",");
						}
					}
			        sb.append("});");
				}; 
		       sb.append("} ," 	   
		    	//onQueueComplete是最后执行的函数,晚于onUploadSuccess
				+"onQueueComplete : function(queueData) { ");
				if(dialog)//默认为true,作用是刷新列表,显示右下角的信息,并且关闭上传页面,dialog为true的话,设置callback函数不起作用
				{
				sb.append("var win = frameElement.api.opener;"  	  
	            +"win.reloadTable();"
	            +"win.tip(serverMsg);"
	            +"frameElement.api.close();");
				}
				else
				{
					//回调函数
				  if(callback!=null)
					  //可传递后台传递的json数据reJsonData
				  sb.append(""+callback+"();");
//				  sb.append(""+callback+";");
				}
				if(view)
				{
				sb.append("$(\"#viewmsg\").html(m.toString());");
				sb.append("$(\"#fileKey\").val(fileKey);");
				}
				sb.append("},");
				//onAllComplete:当所有文件都上传结束后
				sb.append("onAllComplete:function(data){" +
						//"alert(data.filesUploaded );" +
						//"alert('全部上传完毕!');" +
						"},");
				//上传成功处理函数
				sb.append("onUploadSuccess : function(file, data, response) {");
				sb.append("var d=$.parseJSON(data);");
				sb.append("reJsonData = d;");
				//sb.append("alert(d);");
				if(view)
				{
				sb.append("var fileitem=\"<span id=\'\"+d.attributes.id+\"\'><a href=\'#\' onclick=openwindow(\'文件查看\',\'\"+d.attributes.viewhref+\"\',\'70%\',\'80%\') title=\'查看\'>\"+d.attributes.name+\"</a><img border=\'0\' onclick=confuploadify(\'\"+d.attributes.delurl+\"\',\'\"+d.attributes.id+\"\') title=\'删除\' src=\'plug-in/uploadify/img/uploadify-cancel.png\' widht=\'15\' height=\'15\'>  </span>\";");
				sb.append("m.put(d.attributes.id,fileitem);");
				sb.append("fileKey=d.attributes.fileKey;");
				}
				if(onUploadSuccess!=null)
				{
				sb.append(onUploadSuccess+"(d,file,response);");
				}
				sb.append("if(d.success){");
				sb.append("var win = frameElement.api.opener;");
			//	sb.append("tip(d.msg);");
				sb.append("serverMsg = d.msg;");
				sb.append("}");
				sb.append("},");
				sb.append("onFallback : function(){tip(\"您未安装FLASH控件,无法上传图片!请安装FLASH控件后再试\")},");
				sb.append("onSelectError : function(file, errorCode, errorMsg){");
				sb.append("switch(errorCode) {");
				sb.append("case -100:");
				sb.append("tip(\"上传的文件数量已经超出系统限制的\"+$(\'#"+id+"\').uploadify(\'settings\',\'queueSizeLimit\')+\"个文件!\");");
				sb.append("break;");
				sb.append("case -110:"
				+"tip(\"文件 [\"+file.name+\"] 大小超出系统限制的\"+$(\'#"+id+"\').uploadify(\'settings\',\'fileSizeLimit\')+\"大小!\");"
				+"break;"
				+"case -120:"
				+"tip(\"文件 [\"+file.name+\"] 大小异常!\");"
				+"break;"
				+"case -130:"
				+"tip(\"文件 [\"+file.name+\"] 类型不正确!\");"
				+"break;"
				+"}");
		       sb.append("},"
				+"onUploadProgress : function(file, bytesUploaded, bytesTotal, totalBytesUploaded, totalBytesTotal) { "
				//+"tip('<span>文件上传成功:'+totalBytesUploaded/1024 + ' KB 已上传 ,总数' + totalBytesTotal/1024 + ' KB.</span>');"  	  	             
				+"}"
	   			+"});"
				+"});"
				+"function upload() {"
				+"	$(\'#"+id+"\').uploadify('upload', '*');"
				+"		return flag;"
				+"}"
				+"function cancel() {"
				+"$(\'#"+id+"\').uploadify('cancel', '*');"				
				+"}"				
				+"</script>\n");	
		       sb.append("<span id=\""+id+"span\"><input type=\"file\" name=\""+name+"\" id=\""+id+"\" /></span>");
		       if(view)
		       {
		       sb.append("<span id=\"viewmsg\"></span>");
		       sb.append("<input type=\"hidden\" name=\"fileKey\" id=\"fileKey\" />");
		       }
		        
		return sb;
	}

第二步:导入
导入的关键是由callback="forbiddenPage(reJsonData)"发起的。
那么forbiddenPage起什么作用呢,先看一下代码:

	/*****************等待中禁用页面功能*****************/  
/** 
 * 禁用页面 
 */  
function forbiddenPage(reJsonData){  
	//alert('显示遮罩');
    $("<div class=\"datagrid-mask\" style=\"background:#666666;\"></div>").css({display:"block",width:$("body")[0].offsetWidth+10,height:$(window).height()}).appendTo("body");   
    $("<div class=\"datagrid-mask-msg\"></div>").html("正在处理,请稍候……").appendTo("body").css({display:"block",left:($(document.body).outerWidth(true) - 190) / 2,top:($(window).height() - 45) / 2});  
	toImportExcelToDataBase(reJsonData);
}  
/** 
 * 释放页面 
 * @return 
 */  
function releasePage(){  
    $(".datagrid-mask,.datagrid-mask-msg").remove();  
}  

解释一下forbiddenPage的作用:在上传页面展示一个遮罩层,并且展示正在处理的提示,这样在进行导入操作的时候,就能够给用户一个很友好的提示,看一下效果吧



那么toImportExcelToDataBase(reJsonData);是干什么的呢,先上代码:

	//执行将临时文件导入到数据库
	function toImportExcelToDataBase(jsonD){
		url="jformPersonInfoController.do?importPersonData&id="+jsonD.attributes.id+"&importType="+jsonD.attributes.importType;
		$.ajax(
			{
				async : true,
				cache : false,
				url:"jformPersonInfoController.do?importPersonData&id="+jsonD.attributes.id+"&importType="+jsonD.attributes.importType,
				type:"post",
				//如果返回数据只是JSON形式,而不是真正的JSON数据的话,就会获得不到值,为NULL,所以最好不要限制
				//dataType:"json",
				success:function(data){
					//解除遮罩
					releasePage();
					var dt = $.parseJSON(data);
					alertShowInfoTip(dt.msg,'操作提示');
				}
			}
		);
	}

解释一下:就是发送一个请求并且传递了参数,一定要是异步请求(async : true,),不要设定dataType:"json",让ajax自己解析
这个请求就是进行导入操作了,这就不用担心出现前台提示成功,后台一直运行的情况了,这次后台不返回,前台就不会提示和刷新了。
接下来就是看看返回以后的操作了。


success:function(data){
					//解除遮罩
					releasePage();
					var dt = $.parseJSON(data);
					alertShowInfoTip(dt.msg,'操作提示');
				}

releasePage();的作用是解除遮罩层。

/** 
 * 释放页面 
 * @return 
 */  
function releasePage(){  
    $(".datagrid-mask,.datagrid-mask-msg").remove();  
}  

alertShowInfoTip(dt.msg,'操作提示');的作用是什么呢,哒哒哒上代码:

/**
 * 创建一个锁屏,并且有一个确定按钮的弹出框,展示信息
 */
function alertShowInfoTip(msg,title) {
	$.dialog.setting.zIndex = 1980;
	title = title?title:"提示信息";
	$.dialog({
			title:title,
			//icon:'tips.gif',
			lock:true,
			content: msg,
			width:400,
			height:300,
			ok:function(){
				var wind = frameElement.api.opener; 
				frameElement.api.close();
		        wind.reloadTable();
			}
		});
}



看到了吧,就是展示一个提示框,关键是能够显示出导入工程中产生的导入日志信息,让用户明明白白的完成这次导入。这些都完成后,点击确定按钮就会执行关闭上传页面,刷新数据列表。
哒哒哒,到此为止,圆满完成啦。
上图看效果吧:



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值