Java POI实现Excel导入-通用组件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kity9420/article/details/86156759

在工作中,会经常大量使用到Excel导入,如果每个开发人员都自己实现一套Excel导入,带来的问题是研发效率降低,项目中存在大量重复的代码,这时就有必要将Excel导入写成通用组件,开发人员使用该组件,便可轻松实现导入功能
设计思路:
1.将Excel导入设计成模板形式,导入前需要配置此模板
2.通过反射的形式调用对应VO的属性的set方法,给属性赋值
3.返回解析后的结果集

Excel导入模板设计
在src/main/resource/excel/目录下,创建mairuan.import.template.xml文件,将要导入的Excel,配置成模板的形式,内容如下

<?xml version="1.0" encoding="UTF-8"?>
<import>
	<!-- type类型:Byte,Short,Integer,Long,Float,Double,char,String,缺省为String -->
    <sheet id="importRole" startRow="1" class="com.mairuan.base.system.model.RoleVO">
        <column name="RoleCode"  type="String"></column>
        <column name="RoleName"  type="String"></column>
        <column name="RoleDesc"  type="String"></column>
        <column name="RoleState" type="String"></column>
    </sheet>
</import>

说明:
1.一个sheet标签代表一个sheet页
2.startRow代表从哪行开始解析
3.class代表使用哪个对象来承载Excel中的值
4.name 对应class对应中的属性名,首字母必须大写
5.type表示该字段的数据类型,解析Excel时,得到单元格中的数据类型,和我们预想的不一至,会影响通过反射调用set方法,因此使用该字段进行类型转换

创建ImportExcelUtil工具类
1. 在工具类中增加解析mairuan.import.template.xml模板的方法

@SuppressWarnings("unchecked")
private static Element readXML(String sheetId) throws DocumentException, FileNotFoundException {
	File file = ResourceUtils.getFile("classpath:/excel/mairuan.import.template.xml");
	SAXReader reader = new SAXReader();
	Document document = reader.read(file);
	Element root = document.getRootElement();
	Iterator<Element> iterator = root.elementIterator();
	Element element = null;
	while (iterator.hasNext()) {
		element = iterator.next();
		if (sheetId.equals(element.attributeValue("id"))) {
			break;
		}
	}
	return element;
}

根据传入的sheetID,查找对应的标签,如果找到,就反回

2. 在工具类中增加根据type值获得参数类型的方法

private static Class<?> getParameterType(String parameterType) {
		switch (parameterType) {
		case "Byte":
			return Byte.class;
		case "Short":
			return Short.class;
		case "Integer":
			return Integer.class;
		case "Long":
			return Long.class;
		case "Float":
			return Float.class;
		case "Double":
			return Double.class;
		case "char":
			return char.class;
		case "String":
			return String.class;
		case "Date":
			return Date.class;
		default:
			return String.class;
		}
	}

3. 在工具类中增加解析Excel的主方法

@SuppressWarnings("unchecked")
public static List parseExcel(String sheetId, InputStream input) throws Exception {

	// 根据sheetId解析xml,获取Excel模板
	Element element = readXML(sheetId);
	String clazzString = element.attributeValue("class");

	// 从哪行开始解析
	int start = Integer.valueOf(element.attributeValue("startRow"));

	// 反射创建对象实例
	Class<? extends Object> clazz = Class.forName(clazzString);

	List<Element> columns = element.elements();

	Object instance = null;
	// 结果集
	List resultList = new ArrayList();
	// 要调用的方法
	Method method = null;
	// 调用Set方法要传递的参数
	Class<?>[] args = null;

	Cell cell = null;
	XSSFWorkbook wb = null;
	try 
	{
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		// wb = new XSSFWorkbook(file);
		wb = new XSSFWorkbook(input);
		// 获取第一个Sheet页
		XSSFSheet sheet = wb.getSheetAt(0);
		for (int i = start; i <= sheet.getLastRowNum(); i++) {
			Row row = sheet.getRow(i);
			if(row == null)
			{
				continue;
			}
			instance = clazz.newInstance();
			
			StringBuffer buf = new StringBuffer();
			for (int j = 0; j < row.getLastCellNum(); j++) {
				cell = row.getCell(j);
				if (cell == null) {
					continue;
				}
				args = new Class[1];
				args[0] = getParameterType(columns.get(j).attributeValue("type"));
				Object value = getCellValue(cell,args[0],dateFormat);
				
				//累计字符串内容
				buf.append(getStringValue(value));
				
				//Date情况转成String,其他类型保持原样
				args[0] = Date.class.equals(args[0]) ? String.class : args[0];
				
				method = clazz.getDeclaredMethod("set" + columns.get(j).attributeValue("name"), args);
				method.invoke(instance, value);
			}
			
			//整行为空时丢弃该行数据
			if ("".equals(buf.toString().trim()))
			{
				continue;
			}
			//加到集合中
			resultList.add(instance);
		}
	} catch (Exception e) {
		throw e;
	} finally {
		if (wb != null) {
			wb.close();
		}
	}
	return resultList;
}

说明:
1.外层for循环是读取Excel中的行,内层循环是解析每一行数据到对应的VO属性中
2.通过反射的方式给VO属性赋值
3.根据getCellValue方法获得不同类型的数据值,如果类型不对,反射执行赋值会失败
4.如果整行为空,则丢弃这行记录
5.最后将结果集resultList返回

最后完整的ImportExcelUtil工具类如下

package com.mairuan.base.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.ResourceUtils;

public class ImportExcelUtil {

	@SuppressWarnings("unchecked")
	public static List parseExcel(String sheetId, InputStream input) throws Exception {

		// 根据sheetId解析xml,获取Excel模板
		Element element = readXML(sheetId);
		String clazzString = element.attributeValue("class");

		// 从哪行开始解析
		int start = Integer.valueOf(element.attributeValue("startRow"));

		// 反射创建对象实例
		Class<? extends Object> clazz = Class.forName(clazzString);

		List<Element> columns = element.elements();

		Object instance = null;
		// 结果集
		List resultList = new ArrayList();
		// 要调用的方法
		Method method = null;
		// 调用Set方法要传递的参数
		Class<?>[] args = null;

		Cell cell = null;
		XSSFWorkbook wb = null;
		try 
		{
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
			
			// wb = new XSSFWorkbook(file);
			wb = new XSSFWorkbook(input);
			// 获取第一个Sheet页
			XSSFSheet sheet = wb.getSheetAt(0);
			for (int i = start; i <= sheet.getLastRowNum(); i++) {
				Row row = sheet.getRow(i);
				if(row == null)
				{
					continue;
				}
				instance = clazz.newInstance();
				
				StringBuffer buf = new StringBuffer();
				for (int j = 0; j < row.getLastCellNum(); j++) {
					cell = row.getCell(j);
					if (cell == null) {
						continue;
					}
					args = new Class[1];
					args[0] = getParameterType(columns.get(j).attributeValue("type"));
					Object value = getCellValue(cell,args[0],dateFormat);
					
					//累计字符串内容
					buf.append(getStringValue(value));
					
					//Date情况转成String,其他类型保持原样
					args[0] = Date.class.equals(args[0]) ? String.class : args[0];
					
					method = clazz.getDeclaredMethod("set" + columns.get(j).attributeValue("name"), args);
					method.invoke(instance, value);
				}
				
				//整行为空时丢弃该行数据
				if ("".equals(buf.toString().trim()))
				{
					continue;
				}
				//加到集合中
				resultList.add(instance);
			}
		} catch (Exception e) {
			throw e;
		} finally {
			if (wb != null) {
				wb.close();
			}
		}
		return resultList;
	}

	public static boolean checkFileType(String filename) {
		if ((filename == null) || (filename.length() == 0)) {
			return false;
		}

		String type = "";
		int dot = filename.lastIndexOf('.');
		if ((dot > -1) && (dot < (filename.length() - 1))) {
			type = filename.substring(dot + 1);
		}
		if (!"xlsx".equalsIgnoreCase(type)) {
			return false;
		}
		return true;
	}

	@SuppressWarnings("unchecked")
	private static Element readXML(String sheetId) throws DocumentException, FileNotFoundException {
		File file = ResourceUtils.getFile("classpath:/excel/mcc.base.import.xml");
		SAXReader reader = new SAXReader();
		Document document = reader.read(file);
		Element root = document.getRootElement();
		Iterator<Element> iterator = root.elementIterator();
		Element element = null;
		while (iterator.hasNext()) {
			element = iterator.next();
			if (sheetId.equals(element.attributeValue("id"))) {
				break;
			}
		}
		return element;
	}

	private static Object getCellValue(Cell cell,Class<? extends Object> clazz,SimpleDateFormat dateFormat) {
		Object obj = null;
		switch (cell.getCellType()) {
		case Cell.CELL_TYPE_BOOLEAN:
			obj = cell.getBooleanCellValue();
			break;
		case Cell.CELL_TYPE_ERROR:
			obj = cell.getErrorCellValue();
			break;
		case Cell.CELL_TYPE_NUMERIC:
			//用户自定义类型检测
			if(String.class.equals(clazz))
			{
				DecimalFormat df = new DecimalFormat("#.#########"); // 数字格式,防止长数字成为科学计数
				obj = df.format(cell.getNumericCellValue());
			}else if(Date.class.equals(clazz))
			{
				obj = dateFormat.format(cell.getDateCellValue());
			}else if(Double.class.equals(clazz)) {
				obj = cell.getNumericCellValue();
			}else if(Integer.class.equals(clazz)) {
				DecimalFormat df = new DecimalFormat("#.#########"); // 数字格式,防止长数字成为科学计数
				String val = df.format(cell.getNumericCellValue());
				obj = Integer.parseInt(val);
			}
			break;
		case Cell.CELL_TYPE_STRING:
			String cellValue = cell.getStringCellValue();
			
			if (Double.class.equals(clazz)) {
				Double d = Double.valueOf(cellValue);
				obj = d;
			}else if(Integer.class.equals(clazz)) {
				obj = Integer.parseInt(cellValue);
			}else {
				obj=cellValue;
			}
			break;
		default:
			break;
		}
		
		return obj;
	}
	/**
	 * 获取参数类型,默认为String
	 * 
	 * @param parameterType
	 * @return
	 */
	private static Class<?> getParameterType(String parameterType) {
		switch (parameterType) {
		case "Byte":
			return Byte.class;
		case "Short":
			return Short.class;
		case "Integer":
			return Integer.class;
		case "Long":
			return Long.class;
		case "Float":
			return Float.class;
		case "Double":
			return Double.class;
		case "char":
			return char.class;
		case "String":
			return String.class;
		case "Date":
			return Date.class;
		default:
			return String.class;
		}
	}
	
	/**
	 * 获取字符串内容
	 * @param obj
	 * @return
	 */
	private static String getStringValue(Object obj)
	{
		if (obj != null)
		{
			return String.valueOf(obj);
		}
		return "";
	}
}

Spring MVC控制层调用导入工具类解析Excel

@RequestMapping(value = "/importCoreList", method = RequestMethod.POST)
@ResponseBody
public ResponseVO<List<RoleVO>> importExcel(HttpServletRequest request, @RequestParam("file") MultipartFile file)
		throws Exception {

	ResponseVO<List<RoleVO>> response = new ResponseVO<List<RoleVO>>();
	try {
		if (file.isEmpty()) {
			response.setStatus(ResponseVO.failCode);
			response.setMessage("文件为空");
			return response;
		}
		if (!ImportExcelUtil.checkFileType(file.getOriginalFilename())) {
			response.setStatus(ResponseVO.failCode);
			response.setMessage("只支持2007版以上的Excel导入");
			return response;
		}
		String sheetId = "importRole";
		List<RoleVO> result = (List<RoleVO>) ImportExcelUtil.parseExcel(sheetId, file.getInputStream());
		response.setData(result );
	} catch (Exception e) {
		logger.error("Import Excel fail,The message is:", e);
		response.setStatus(ResponseVO.failCode);
	}
	return response;
}

开发人员只需要通过List result = (List) ImportExcelUtil.parseExcel(sheetId, file.getInputStream());就可以将Excel中的值转换成想要的VO对应,进行后面的数据操作

猜你喜欢

转载自blog.csdn.net/kity9420/article/details/86156759