码迷,mamicode.com
首页 > 其他好文 > 详细

excel导入导出优化

时间:2015-08-21 23:20:26      阅读:311      评论:0      收藏:0      [点我收藏+]

标签:

        对于上一篇excel中出现的问题,在excel导入导出中都做了优化.还是eclipse+jdk1.8,但是这个项目是一个web项目,需要配合Tomcat服务器,并且使用了SSH框架,

I/O操作过多

        首先,对于I/O操作过多,那么就不像之前一样,一条一条的添加或者更新;而且凑齐一堆,也就是一个list集合,然后统一的批量保存.

        使用SessionFactory获取当前的session,然后调用session的persist方法,保存实体.只是设置了一个批量的量值.每到30条数据,就将缓存同步到数据库中.

	/**
	 * 批量保存
	 */
	@Override
	public <T> boolean saveEntitys(List<T> entitys) {
		boolean flag = false;
		int batchCount = 30;
		try {
			for (int i = 1; i <= entitys.size(); i++) {
				getSession().persist(entitys.get(i - 1));
				if (i % batchCount == 0) {
					getSession().flush();
					getSession().close();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return flag;
	}

Excel中英文

      然后是excel模板必须固定,不能更换列的顺序,且是英文字段的问题.excel模板必须固定是因为我们没有从excel中获取表头,并记录每个表头是在那一列,这样就可以根据表头中的值,知道列号,然后根据列号去相应的列中取值.

// 将列名和列号放入Map中,这样下面通过列名就能获取列号,
LinkedHashMap<String, Integer> colNameNoMap = new LinkedHashMap<String, Integer>();
for (int i = 0; i < excelFiledNameArray.length; i++) {
	colNameNoMap.put(excelFiledNameArray[i],
			headerRow[i].getColumn());
}

       而英文问题,需要有一个中文对照表,这里就将中文和英文的对照表放到xml文件中.在WebContent文件夹下,放着studentImport.xml的配置文件.内容就是如下,这里面设置了需要导入的excel的实体,导入的excel中的Sheet名称,以及导入的普通字段是中英文对照哪些,主外键关联的字段是哪些(目前还没弄出来),以及唯一不可重复的字段是哪些.而这些配置了的字段,不论是普通字段,主外键关联字段还是唯一键字段都是excel中必须存在的字段的

<?xml version="1.0" encoding="UTF-8"?>

<!--导入"Student"(下载Excel模板 和 导入Student Excel) -->
<importExcel>
	<Entity entityName="com.tgb.entity.Student" sheetName="考生">
		<!--1:NormalFieldMaps——要导入的实体的 普通字段。。。Start。。。 -->
		<NormalFieldMaps>
			<NormalField name="学号" code="stuNo"></NormalField>
			<NormalField name="姓名" code="stuName"></NormalField>
			<NormalField name="性别" code="stuSex"></NormalField>
			<NormalField name="学院" code="department"></NormalField>
			<NormalField name="专业" code="major"></NormalField>
		</NormalFieldMaps>
		<!--1:NormalFieldMaps——要导入的实体的 普通字段。。。End。。。 -->

		<!--2:UniqueFieldMaps——要导入的实体中,数据不可重复的字段。。。Start。。。 -->
		<UniqueFieldMaps>
			<UniqueField name="学号">
			</UniqueField>
		</UniqueFieldMaps>
		<!--2:UniqueFieldMaps——要导入的"教师"实体中,数据不可重复的字段。。。End。。。 -->
	</Entity>
</importExcel>

      而我们需要做的就是用Jdom或者Dom4j工具,读取这个xml文件,获取根节点importExcel,以及他的子节点Entity,可以是多个.并且Entity中的属性,是什么也要获取到.用entityName可以反射出实体的实例,而子节点NormalFieldMaps和UniqueFieldMaps可以将里面的中英文字段对照表放到map中,以及放到list中.只需要解析NormalField节点的属性name和code,以及UniqueField的name属性.

	// 记录普通节点
	private LinkedHashMap<String, String> normalFieldMap;
	// 记录唯一节点
	private List<String> uniqueFieldList;
	// 数组的形式记录唯一节点
	private String[] uniqueFieldArray = null;

	/******************** 解析{导入xml}到map中 start *******************************/
	/**
	 * 将导入的excel解析
	 * 
	 * @param inXMLFile
	 */
	private void importExcelXml2Map(InputStream inXMLFile) {

		try {
			// 1.检测
			if (null == inXMLFile) {
				throw new FileNotFoundException();
			}

			// 2.解析,使用dom4j
			SAXReader reader = new SAXReader();
			Document doc = reader.read(inXMLFile);
			// 根元素,importExcel
			Element root = doc.getRootElement();

			// 3.获取Entity节点
			Iterator iterator = root.elements("Entity").iterator();
			while (iterator.hasNext()) {
				Element entityElement = (Element) iterator.next();
				// 3.1.获取属性,entityName,以及导入的实体
				String entityName = entityElement.attributeValue("entityName");
				entityClazz = Class.forName(entityName);

				// 实例化,普通字段的map和唯一字段的list,用于放xml中的值
				// 链表的Map,空间不连续,添加,修改,删除快,读不快
				normalFieldMap = new LinkedHashMap<String, String>();
				uniqueFieldList = new ArrayList<String>();

				// 解析普通字段节点
				parseEntityNormalFieldMaps(entityElement);
				// 解析唯一字段节点
				parseEntityUniqueFieldMaps(entityElement);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 解析普通字段的Map集合节点
	 * 
	 * @param entityElement
	 */
	private void parseEntityNormalFieldMaps(Element entityElement) {
		if (null != entityElement) {
			Iterator iterator = entityElement.elements("NormalFieldMaps")
					.iterator();
			while (iterator.hasNext()) {
				Element normalFiledMapsElement = (Element) iterator.next();
				parseNormalFieldMaps(normalFiledMapsElement);
			}
		}
	}

	/**
	 * 解析普通字段,在Map集合下的
	 * 
	 * @param normalFiledMapsElement
	 */
	private void parseNormalFieldMaps(Element normalFiledMapsElement) {
		if (null != normalFiledMapsElement) {
			Iterator iterator = normalFiledMapsElement.elements("NormalField")
					.iterator();
			while (iterator.hasNext()) {
				Element normalFileElement = (Element) iterator.next();
				// 将节点上的值,赋值到Map中
				setNormalFieldMap(normalFileElement);
			}
		}

	}

	/**
	 * 将普通字段的name和code,赋值到normalFieldMap上
	 * 
	 * @param normalFileElement
	 */
	private void setNormalFieldMap(Element normalFileElement) {
		if (null != normalFileElement) {
			// 字段中文
			String name = normalFileElement.attributeValue("name");
			// 字段英文
			String code = normalFileElement.attributeValue("code");
			normalFieldMap.put(name, code);
		}

	}

	/**
	 * 解析唯一字段的Map集合节点
	 * 
	 * @param entityElement
	 */
	private void parseEntityUniqueFieldMaps(Element entityElement) {
		if (null != entityElement) {
			Iterator iterator = (Iterator) entityElement.elements(
					"UniqueFieldMaps").iterator();
			while (iterator.hasNext()) {
				Element uniqueFieldMapsElement = (Element) iterator.next();
				parseUniqueFieldMaps(uniqueFieldMapsElement);
			}
		}
	}

	/**
	 * 解析唯一字段,在map集合下的
	 * 
	 * @param uniqueFieldMapsElement
	 */
	private void parseUniqueFieldMaps(Element uniqueFieldMapsElement) {
		if (null != uniqueFieldMapsElement) {
			Iterator iterator = (Iterator) uniqueFieldMapsElement.elements(
					"UniqueField").iterator();
			while (iterator.hasNext()) {
				Element uniqueFieldElement = (Element) iterator.next();
				setUniqueFieldList(uniqueFieldElement);
			}
			setUniqueFieldArray(uniqueFieldList);
		}
	}

	/**
	 * 将唯一字段的值设置到list中
	 * 
	 * @param uniqueFieldElement
	 */
	private void setUniqueFieldList(Element uniqueFieldElement) {
		if (null != uniqueFieldElement) {
			// 唯一字段的中文名,对应普通字段的中文名
			String name = uniqueFieldElement.attributeValue("name");
			uniqueFieldList.add(name);
		}
	}

	/**
	 * 将list中的唯一值,放到Array数组中
	 * 
	 * @param uniqueFieldList
	 */
	private void setUniqueFieldArray(List<String> uniqueFieldList) {
		if (null != uniqueFieldList) {
			int size = uniqueFieldList.size();
			uniqueFieldArray = new String[size];

			for (int i = 0; i < size; i++) {
				uniqueFieldArray[i] = uniqueFieldList.get(i);
			}
		}
	}

	/******************** 解析{导入xml}到map中 end *********************************/

       这样就可以提供这样的excel模板了.表头变为中文,不用写id,hibernate可以用主键生成策略生成id.并且各列的顺序也可以随意,只是必须有这些字段.

技术分享

Excel路径 

       而excel的路径写死的问题,只需要一个界面,上传工具.下载的时候通过response放到界面上.

文件上传

       jsp页面中的内容,提供上传功能.

<body>
	<form id="importExcelForm" enctype="multipart/form-data" method="post" >
		<input id="fileBox" name="fileBox" class="easyui-filebox" style="width:400px" data-options="prompt:'请选择文件'"/>
		<a id="importButton" href="#" class="easyui-linkbutton" onclick="importExcel()">导入Excel</a>
	</form>
	
	<script type="text/javascript">
		//点击弹出选择文件框的按钮
		$('#fileBox').filebox({
			buttonText :'选择文件',
			buttonAlign :'right'
		});
		
		function importExcel() {
			//获取文件
			var fileName = $('#fileBox').filebox('getValue');
			//基本的校验
			if(fileName == "") {
				alert("请选择上传的文件!");
			}else {
				var fileNameSuf = /\.[^\.]+$/.exec(fileName);
  				if(fileNameSuf != ".xls") {
					alert("请选择xls格式文件!");
					$("#fileBox").filebox('setValue','');
				}else {
					$('#importExcelForm').form('submit',{
						url:"${pageContext.request.contextPath}/importExcel",
						onSubmit:function() {
							return $(this).form('validate');
						},
						success:function(result) {
							alert(result)
							var result = eval(result);
							if(result == false) {
								$.messager.alert("提示","导入失败,请检查excel数据!","info");
							}else if(result == true) {
								$.messager.alert("提示","导入成功!","info");
							}else {
								$.messager.alert("提示","浏览器不兼容,请更换浏览器","info");
							}
						}
					});
				}
			}
		}
		
	</script>

      而Contorller接收到的上传请求,会将excel和导入xml都变为输入流,然后调用工具类的导入方法,将excel导入.导入方法,主要也是解析导入xml,获取里面的配置,以及获取excel中的数据,将数据变为实体集合,最后将实体集合使用批量保存,保存到数据库中.当然,将excel的数据变成list的时候,需要对数据进行检测,若配置了唯一字段,则该字段中的值,必须是唯一的.

	/**
	 * 导入Excel
	 */
	@RequestMapping(value="importExcel")
	private void importExcel(
			@RequestParam("fileBox") CommonsMultipartFile excelFile,
			HttpServletRequest request,HttpServletResponse response) {
		//接收excel文件变为输入流
		InputStream inExcelFile = null;
		try {
			 inExcelFile = excelFile.getInputStream();
		} catch (IOException e) {
			System.err.println("excel读取出错。");
			e.printStackTrace();
		}
		
		//获取xml配置文件,也变为输入流
		String xmlFilePath = request.getSession().getServletContext().getRealPath("/")+"studentImport.xml";
		File importXMLFile = new File(xmlFilePath);
		InputStream inXMLFile  = null;
		try {
			inXMLFile = new FileInputStream(importXMLFile);
		} catch (FileNotFoundException e) {
			System.err.println("未找到xml文件。");
			e.printStackTrace();
		}
		
		try {
			//导入excel到数据库中,使用工具类parseImportExcelXml
			Boolean flag = parseImportExcelXml.importExcel(inExcelFile,inXMLFile,response);
			jacksonJsonUntil.beanToJson(response, flag);
		}catch(Exception e) {
			System.err.println("excel导入失败。");
			e.printStackTrace();
		}
		
	}

文件下载

          下载到界面上,这里创建Workbook不是写一个文件路径,而是写response的输出流.然后添加表头,添加内容,设置excel名,以及response的一些参数,内容类型和头文件.

errorLines = errorMap.size();

// 创建错误列表工作簿和sheet
WritableWorkbook ewwb = Workbook.createWorkbook(response
		.getOutputStream());
WritableSheet eSheet = ewwb.createSheet("错误列表", 0);

// 添加表头
for (int n = 0; n < excelFiledNameArray.length; n++) {
	Label label = new Label(n, 0, excelFiledNameArray[n]);
	eSheet.addCell(label);
}

// 并添加两列,错误原因和所在行号
eSheet.addCell(new Label(excelFiledNameArray.length, 0, "错误原因"));
eSheet.addCell(new Label(excelFiledNameArray.length + 1, 0,
		"所在行号"));

// 添加内容
... addErrorRow(sheet, eSheet, errorMap);

// 设置文件名,当前时间
String fileName = new SimpleDateFormat("yyyyMMddhhmmss")
		.format(new Date()).toString() + "ErrorData";
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
		"attachment;filename=" + fileName + ".xls");

//设置错误excel的单元格大小
setColumnAutoSize(eSheet, 10);

//写入数据,并关闭工作簿
ewwb.write();
ewwb.close();

附注

运行的效果

技术分享

       点击导入Excel,导入成功,数据库中的数据如下.

技术分享

   再次导入Excel,由于数据重复,所以,生成错误excel,并且数据不写入数据库中.

 

技术分享

项目下载:Excel导入导出优化


 





 

 

 

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

excel导入导出优化

标签:

原文地址:http://blog.csdn.net/liuyanlinglanq/article/details/47833151

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!