最近在研究JEECG,遇到一个问题,就是数据导入。
官方版本提供的实例中的EXCEL 导入是用的uploadify插件,该插件提供了一系列的好用的方法接口,能够灵活的处理过程中的各个环节,但是有个问题是,uploadify只是负责客户端的上传监控,他检测着要是上传完毕了,就会去执行onUploadSuccess方法和onQueueComplete对应的方法。如果上传的文件小并且导入时间短的话,不会有什么问题,在前台执行这两个方法的时候后台能够返回执行结果,但是如果导入的文件比较大,就有问题了。
如果文件比较大,uploadify上传完成文件之后就会默认为执行完毕可以返回结果了,但是其实后面还要进行复杂的长时间的导入(插入数据库)操作,这样uploadify就不会等你后面的操作了,他会直接就去执行onUploadSuccess方法和onQueueComplete。这样导致的结果是,在前台看来导入完成了,导入界面关闭了,导入列表刷新了,但是其实后台还在执行着导入操作。这样对于用户来讲是很不友好的。用户在前台看到的是执行完毕,但是导入的数据跟自己excel中的数据不一致,但是又没有报错(当然,这种情况官网提供的实例tip提示也不会有任何内容),其实后台还没有执行完毕,并且如果导入出现问题也没法在前台进行展示了。
这个问题纠结了好几天,怎么解决呢!
想到了在工作中做过的数据导入(用的是JSF框架),原理是将数据的上传和数据的导入分开来操作,上传专门上传,导入专门导入。
问题解决设想:
导入分两步完成:
第一步:上传
用uploadify进行上传,上传后将文件保存到服务器的某个临时文件中,返回上传结果,结束本次请求
第二步:导入
上次请求结束后,紧接着利用AJAX的异步性,发起一个请求,该请求专门负责将上一步上传的文件 进行导入操作。这一步就会等到该请求真正返回结果后才会在前台,完成本次请求,关闭上传页面,刷新数据列表。
其实对于用户来讲还是一步完成。
到此为止就能完美的解决以上问题。
问题解决实现:
第一步:上传
注意:dialog设置为false,因为dialoag的默认值为true,必须明确的设定dialog为false,
那么dialog的作用是什么呢,如果dialog为true,就会执行刷新数据列表,关闭上传页面,并且弹出提示信息,并且callback函数用于没有机会执行。这些在我们没有真正执行完成,我们是不希望这样操作的。
设置callback回调函数,该函数是在执行onQueueComplete(可以看做是upladify最后执行的函数)的时候执行的,这样问题的思路就明确了,就是在第一步执行完成上传之后,不是进行刷新列表,关闭上传页面,弹出信息,而是执行回调函数
注:本人修改了官方的upload标签,增加了reJsonData公共变量, 将onUploadSuccess返回的json数据赋值给reJsonData,然后在回调函数进行传递:
修改UploadTag.java
第二步:导入
导入的关键是由callback="forbiddenPage(reJsonData)"发起的。
那么forbiddenPage起什么作用呢,先看一下代码:
解释一下forbiddenPage的作用:在上传页面展示一个遮罩层,并且展示正在处理的提示,这样在进行导入操作的时候,就能够给用户一个很友好的提示,看一下效果吧
那么toImportExcelToDataBase(reJsonData);是干什么的呢,先上代码:
解释一下:就是发送一个请求并且传递了参数,一定要是异步请求(async : true,),不要设定dataType:"json",让ajax自己解析
这个请求就是进行导入操作了,这就不用担心出现前台提示成功,后台一直运行的情况了,这次后台不返回,前台就不会提示和刷新了。
接下来就是看看返回以后的操作了。
releasePage();的作用是解除遮罩层。
alertShowInfoTip(dt.msg,'操作提示');的作用是什么呢,哒哒哒上代码:
看到了吧,就是展示一个提示框,关键是能够显示出导入工程中产生的导入日志信息,让用户明明白白的完成这次导入。这些都完成后,点击确定按钮就会执行关闭上传页面,刷新数据列表。
哒哒哒,到此为止,圆满完成啦。
上图看效果吧:
官方版本提供的实例中的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();
}
});
}
看到了吧,就是展示一个提示框,关键是能够显示出导入工程中产生的导入日志信息,让用户明明白白的完成这次导入。这些都完成后,点击确定按钮就会执行关闭上传页面,刷新数据列表。
哒哒哒,到此为止,圆满完成啦。
上图看效果吧: