excel导入导出优化

对于上一篇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导入导出优化

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

时间: 2024-08-11 00:16:43

excel导入导出优化的相关文章

Excel导入导出的业务进化场景及组件化的设计方案(转)

1:前言 看过我文章的网友们都知道,通常前言都是我用来打酱油扯点闲情的. 自从写了上面一篇文章之后,领导就找我谈话了,怕我有什么想不开. 所以上一篇的(下)篇,目前先不出来了,哪天我异地二次回忆的时候,再分享分享. 话说最近外面IT行情飞涨还咋的,人都飞哪去了呢,听说各地的军情都进入紧急状态了. 回归下正题,今天就抽点时间,写写技术文,和大伙分享一下近年在框架设计上的取的一些技术成果. 2:项目背景 在针对运营商(移动.联通.电信.铁塔)的信息类的系统中,由于相关的从业人员习惯于Excel的办公

利用反射实现通用的excel导入导出

如果一个项目中存在多种信息的导入导出,为了简化代码,就需要用反射实现通用的excel导入导出 实例代码如下: 1.创建一个 Book类,并编写set和get方法 1 package com.bean; 2 3 public class Book { 4 private int id; 5 private String name; 6 private String type; 7 // public int a; 8 9 public String getType() { 10 System.ou

POI实现excel导入导出

1.分析excel import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.Ce

[Utils]POI实现excel导入导出

1.分析excel 2.poi工具类 import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.s

Java - Excel 导入 导出,报无效数据

用Java,做了套后台账号管理系统,内有Excel导入导出模块 后台管理员登录后,可以通过<input type="file">,读取指定格式的Excel工作薄 系统会解析读取到的Excel,获取工作薄中,各工作表的数据 当然,如果 上传文件,不是Excel文件,则会显示上传失败 或是 是Excel文件,但其内工作表的数据不符合要求,则会将不符合要求的数据行,自动过滤掉 问题来了: 记得以前测试的时候,是没问题的,但今天,把导出文件,再次导入时,就一直报无效数据 (别提当时

一个基于POI的通用excel导入导出工具类的简单实现及使用方法

前言: 最近PM来了一个需求,简单来说就是在录入数据时一条一条插入到系统显得非常麻烦,让我实现一个直接通过excel导入的方法一次性录入所有数据.网上关于excel导入导出的例子很多,但大多相互借鉴.经过思考,认为一百个客户在录入excel的时候,就会有一百个格式版本,所以在实现这个功能之前,所以要统一excel的格式.于是提供了一个通用excel模版的下载功能.当所有客户用模版录入好数据再上传到系统,后端对excel进行解析,然后再持久化到数据库. 概述: 此工具类的几大特点 1.基本导入导出

Excel导入导出帮助类

/// <summary>    /// Excel导入导出帮助类    /// 记得引入 NPOI    /// 下载地址   http://npoi.codeplex.com/releases/    /// </summary> public class ExcelHelper    {        #region 导出Excel /// <summary>        /// 导出Excel  注:model 字段必须加[DisplayName("

java poi excel 导入导出数据

背景:1.pringmvc 框架下 的excel 导入导出   2.OI 操作office. 页面代码: <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true"> <div class=&quo

开发指南专题十五:JEECG微云快速开发平台EXCEL导入导出

 开发指南专题十五:JEECG微云快速开发平台EXCEL导入导出 14.EXCEL导入导出 Excel的导入导出抽取通用功能,简化大家对POI的操作,对实体对象进行简单的注解配置就可以完成导入导出,模板的使用更是可以让打造漂亮的Excle报表,从而使大家从重复的工作中解脱出来,更加关注与业务的处理. 14.1注解介绍    注解名 作用对象 描述 是否必须 Excel 字段 对Excel字段的cell属性设置 是 ExcelCollection 字段 对集合对象进行标记表示一对多导出 否 E