Java POI利用反射导入导出Excel、Jxls实现Excel模板导出

初衷:项目中一直没有一个好用的Excel导入导出功能。所以简单实现了导入导出功能分享大家一起讨论和学习

关于本项目说明

  1. 导入:基于poi导入做了一层封装、支持注解方式标识属性对应Excel列、并支持简单规则校验、提供校验规则自定义
  2. 两种导出:一种基于poi的导出,一种基于jxls模板导出。jxls模板导出可参考:jxls说明
  3. 别的就不多BB了,直接上代码。源码包可直接下载使用:点我取源码 没有过多的文字说明,全是代码下载看源码便知

1、基于maven项目的添加依赖,POM依赖如下


	
		commons-beanutils
		commons-beanutils
		1.8.0
	
	
		org.apache.poi
		poi-ooxml
		3.9
	
	
	
		net.sf.jxls
		jxls-core
		1.0.6
	
	
		net.sf.jxls
		jxls-reader
		1.0.6
	
	
	    org.apache.ant
	    ant
	    1.9.4
	

 2、Excle导入类ExcelException.java,ExcelFieldMeta.java,ExcelUtil.java。入口类为ExcelUtil.java提供各种静态方法

ExcelUtil.java

/**
 * Excel导入
 * 
 * @author http://blog.csdn.net/make_a_difference
 */
@SuppressWarnings("unchecked")
public class ExcelUtil {

	private static Log logger = LogFactory.getLog(ExcelUtil.class);

	// 换行标识
	private final static String ENTER_STR = "\n";

	/**
	 * 默认解析Excel第一个sheet页
	 * 
	 * @param is
	 *            输入流
	 * @param startRow
	 *            开始解析行
	 * @param clazz
	 *            实体类
	 * @return
	 * @throws ExcelException
	 */
	public static  List excelParsing(InputStream is, Integer startRow, Class clazz) throws ExcelException {
		return excelParsing(is, 1, startRow, clazz);
	}

	/**
	 * 默认解析Excel第一个sheet页
	 * 
	 * @param is
	 *            输入流
	 * @param startRow
	 *            开始解析行
	 * @param clazz
	 *            实体类
	 * @return
	 * @throws ExcelException
	 * @throws FileNotFoundException
	 */
	public static  List excelParsing(File file, Integer startRow, Class clazz) throws ExcelException {
		FileInputStream is;
		try {
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			logger.error("Excel文件异常", e);
			throw new ExcelException("Excel文件异常");
		}
		return excelParsing(is, 1, startRow, clazz);
	}

	/**
	 * 解析Excel
	 * 
	 * @param is
	 *            输入流
	 * @param sheetIndex
	 *            sheet页数
	 * @param startRow
	 *            开始解析行
	 * @param clazz
	 *            实体类
	 * @return
	 * @throws ExcelException
	 */
	public static  List excelParsing(InputStream is, Integer sheetIndex, Integer startRow, Class clazz)
			throws ExcelException {
		// 1: 获取工作簿
		Workbook wb = null;
		try {
			wb = ExcelUtil.getWorkbook(is);
		} catch (Exception e) {
			logger.error("系统解析Excel获取WorkBook错误", e);
			throw new ExcelException("系统解析Excel错误");
		}
		// 2:获取第一个sheet页码
		Sheet sheet = wb.getSheetAt(sheetIndex - 1);
		return doParsing(sheet, startRow, clazz);
	}

	@SuppressWarnings("rawtypes")
	private static  List doParsing(Sheet sheet, Integer startRow, Class clazz) throws ExcelException {
		StringBuffer errorMessage = new StringBuffer("");
		// 1:获取sheet页的总行数
		int lastRow = sheet.getLastRowNum();
		// 2: 行解析,默认从0开始,startRow属性定义了数据从该行开始解析,程序中定义的行数从1开始算,所以此处要-1
		List resultList = new ArrayList(3);
		try {
			for (int rowIndex = startRow - 1; rowIndex <= lastRow; rowIndex++) {
				Row row = sheet.getRow(rowIndex); // 得到 第 n 行
				// 2.1获取的列为空则跳出循环
				if (row == null) {
					continue;
				}
				// 2.2 解析对象
				Object rowBean = clazz.newInstance();
				String rowErrorMessage = check(row, rowIndex, rowBean);
				// 2.3 如果返回的不是null,则存在错误信息则记录错误信息
				if (rowErrorMessage != null) {
					errorMessage.append(rowErrorMessage + ENTER_STR);
					continue;
				} else if ("end".equals(rowErrorMessage)) {
					resultList.add(rowBean);
					return resultList;
				}
				// 2.4 如果正确 则不读取
				resultList.add(rowBean);
			}
		} catch (Exception e) {
			logger.error("系统解析Excel列错误", e);
			throw new ExcelException("系统解析Excel错误");
		}
		// 3:如果存在错误则需要将异常抛出
		if (errorMessage.length() > 0) {
			throw new ExcelException(errorMessage.toString());
		}
		return resultList;
	}

	/**
	 * 
	 * @param row
	 *            行信息
	 * @param rowIndex
	 *            当前行数
	 * @param declaredFields
	 * @param rowBean
	 * @return 错误信息
	 * @throws Exception
	 */

	private static String check(Row row, int rowIndex, Object rowBean) throws Exception {
		// 1: 对象中循环取数据并赋值
		for (Field field : rowBean.getClass().getDeclaredFields()) {
			// 2:包含excel规则校验的注解
			ExcelFieldMeta meta = field.getAnnotation(ExcelFieldMeta.class);
			// 3:单元格的值
			Cell cell = row.getCell(meta.cell() - 1);
			String cellValue = getCellValue(cell);
			Object obejctVal = null;
			// 3.1 第一个单元并单元格值为空则表示读取excel结束
			if (meta.cell() == 1 && (cell == null || "".equals(cellValue) || "null".equals(cellValue)))
				return "end";
			// 4.2校验单元格是否必填,单元格可以为空
			if (!meta.isNotNull() && (cellValue == null || "".equals(cellValue))) {
				continue;
			}
			// 4.2:可以为空且为空
			if (meta.isNotNull() && (cellValue == null || "".equals(cellValue))) {
				return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容不能为空值";
			}
			// 4.3 整型、浮点型
			if (field.getType() == Integer.class) {
				try {
					// 此处采用浮点数转换,验证value中是否含有字符。整形转换时只要整数部分的数据
					obejctVal = (int) cell.getNumericCellValue();
				} catch (Exception ne) {
					return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容只能为数值,读取时为:" + cellValue;
				}
			} else if (field.getType() == Double.class) {// 4.4 浮点型
				try {
					// 此处采用浮点数转换,验证value中是否含有字符。整形转换时只要整数部分的数据
					obejctVal = cell.getNumericCellValue();
				} catch (NumberFormatException ne) {
					return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容只能为数值,读取时为:" + cellValue;
				}
			} else if (field.getType() == Date.class) {// 4.5处理日期类型
				SimpleDateFormat sf = new SimpleDateFormat(meta.dateFormat());
				try {
					obejctVal = sf.parse(cellValue);
					obejctVal.toString();
				} catch (ParseException e) {
					return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容中日期格式异常,读取时为" + cellValue;
				}
			} else if (field.getType() == String.class) {
				obejctVal = cellValue;
			}
			// 4.6 长度校验
			if (meta.maxLength() != -1 && cellValue.length() > meta.maxLength()) {
				return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容的长度不能超出" + meta.maxLength() + "个字符";
			}
			// 4.7设置属性
			PropertyUtils.setProperty(rowBean, field.getName(), obejctVal);
		}
		return null;
	}

	/**
	 * 得到当前列的值
	 * 
	 * @param cell
	 * @return
	 */
	private static String getCellValue(Cell cell) {
		String typeString = "";
		if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
			typeString = String.valueOf(cell.getNumericCellValue());
		} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
			typeString = String.valueOf(cell.getStringCellValue());
		} else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
			typeString = String.valueOf(cell.getDateCellValue());
		} else if (cell.getCellType() == Cell.CELL_TYPE_BLANK) {
			typeString = String.valueOf(cell.getStringCellValue());
		} else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
			typeString = "";
		}
		return typeString;
	}

	/**
	 * 解析不同版本的Excel文件
	 * 
	 * @param in
	 * @return
	 * @throws IOException
	 * @throws InvalidFormatException
	 */
	private static Workbook getWorkbook(InputStream in) throws IOException, InvalidFormatException {
		if (!in.markSupported()) {
			in = new PushbackInputStream(in, 8);
		}
		if (POIFSFileSystem.hasPOIFSHeader(in)) {
			return new HSSFWorkbook(in);
		}
		if (POIXMLDocument.hasOOXMLHeader(in)) {
			try {
				return new XSSFWorkbook(OPCPackage.open(in));
			} catch (InvalidFormatException e) {
				logger.error("Excel文件异常", e);
			}
		}
		throw new IllegalArgumentException("你的excel版本目前poi不支持");
	}

ExcelFieldMeta.java

/**
 * Excel对象自定义注解
 * 
 * @author http://blog.csdn.net/make_a_difference
 *
 */
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ ElementType.FIELD, ElementType.METHOD }) // 定义注解的作用目标**作用范围字段、枚举的常量/方法
@Documented // 说明该注解将被包含在javadoc中
public @interface ExcelFieldMeta {

	// 对应Excel文档中的列号
	int cell() default 0;

	// 不能为空,默认为 是
	boolean isNotNull() default true;

	// 最大长度, -1 则不限制
	int maxLength() default -1;

	// 日期格式,默认导入格式为"yyyy/MM/dd"如"2015/5/13"
	String dateFormat() default "yyyy/MM/dd";
}

ExcelException.java

/**
 * Excel处理异常
 * 
 * @author http://blog.csdn.net/make_a_difference
 */
public class ExcelException extends Exception {

	/**
	 * @Fields serialVersionUID: (用一句话描述这个变量表示什么)
	 */
	private static final long serialVersionUID = 1L;

	public ExcelException() {
		super();
	}

	public ExcelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public ExcelException(String message, Throwable cause) {
		super(message, cause);
	}

	public ExcelException(String message) {
		super(message);
	}

	public ExcelException(Throwable cause) {
		super(cause);
	}

}

3、Excel基于POI导出参考代码

/**
 * Excel下载
 * 
 * @author http://blog.csdn.net/make_a_difference
 */
public class ExcelExport {

	/**
	 * 下载excel文件,内容使用MAP存放 。 支持多个sheet。两个map的长度必须一致,map的key为索引(index)
	 * 
	 * @param response
	 *            响应流
	 * @param headName
	 *            Excel表名
	 * @param tableHeadMap
	 *            每个sheet对应的表头为具体的value值
	 * @param tableBodyMap
	 *            每个sheet对应的内容为具体的value值
	 */
	public static void downloadExcelMap(HttpServletResponse response, String headName,
			Map> tableHeadMap, Map>> tableBodyMap)
			throws Exception {
		headName = replaceAllSpecial(headName);
		// 1:创建一个workbook
		Workbook workbook = new HSSFWorkbook();

		// 创建样式
		CellStyle style = workbook.createCellStyle();
		Font font = workbook.createFont();
		font.setBoldweight(Font.BOLDWEIGHT_BOLD); // 粗体
		style.setFont(font);
		style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
		style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
		style.setBorderTop((short) 1);
		style.setBorderBottom((short) 1);
		style.setBorderLeft((short) 1);
		style.setBorderRight((short) 1);

		// 设置合计样式
		CellStyle style1 = workbook.createCellStyle();
		style1.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
		style1.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
		style1.setBorderTop((short) 1);
		style1.setBorderBottom((short) 1);
		style1.setBorderLeft((short) 1);
		style1.setBorderRight((short) 1);

		int sheetLength = tableHeadMap.size();
		if (sheetLength != tableBodyMap.size()) {
			throw new Exception("tableHeadMap与tableBodyMap对应的长度不一致");
		}
		for (int i = 0; i < sheetLength; i++) {
			Sheet sheet = workbook.createSheet(headName + (i + 1));
			List tableHead = tableHeadMap.get(i);
			List> tableBody = tableBodyMap.get(i);
			// 2:合并单元格,表头。并设置值
			CellRangeAddress cra = new CellRangeAddress(0, 0, 0, tableHead.size() - 1);
			sheet.addMergedRegion(cra);
			Row row = sheet.createRow(0);
			Cell tableName = row.createCell(0);
			tableName.setCellStyle(style);
			tableName.setCellValue(headName);

			// 3:设置表head
			Row row1 = sheet.createRow(1);
			for (int m = 0; m < tableHead.size(); m++) {
				Cell createCell = row1.createCell(m);
				createCell.setCellValue(tableHead.get(m));
				createCell.setCellStyle(style);
			}
			// 4:表格内容
			for (int m = 0; m < tableBody.size(); m++) {
				Row rows = sheet.createRow(m + 2);
				int j = 0;
				for (Map.Entry entry : tableBody.get(m).entrySet()) {
					Cell createCell = rows.createCell(j);
					if (entry.getValue() != null && !"".equals(entry.getValue())) {
						createCell.setCellValue(entry.getValue().toString());
					} else {
						createCell.setCellValue("");
					}
					createCell.setCellStyle(style1);
					j++;
				}
			}
		}

		// 5:设置头
		response.setHeader("Content-disposition",
				"attachment; filename=" + new String(headName.getBytes("GB2312"), "ISO8859-1") + ".xls");
		// 6:设置头类型
		response.setContentType("application/vnd.ms-excel");
		// 7:写出
		OutputStream toClient = response.getOutputStream();
		workbook.write(toClient);
		toClient.flush();
		toClient.close();

	}

	/**
	 * 下载excel文件,内容使用MAP存放
	 * 
	 * @param response
	 *            响应流
	 * @param headName
	 *            Excel文件名
	 * @param tableHead
	 *            表的title
	 * @param tableBody
	 *            表内容
	 * @throws IOException
	 */
	public static void downloadExcelMap(HttpServletResponse response, String headName, List tableHead,
			List> tableBody) throws IOException {
		headName = replaceAllSpecial(headName);
		// 1:创建一个workbook
		Workbook workbook = new HSSFWorkbook();

		// 创建样式
		CellStyle style = workbook.createCellStyle();
		Font font = workbook.createFont();
		font.setBoldweight(Font.BOLDWEIGHT_BOLD); // 粗体
		style.setFont(font);
		style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
		style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
		style.setBorderTop((short) 1);
		style.setBorderBottom((short) 1);
		style.setBorderLeft((short) 1);
		style.setBorderRight((short) 1);

		// 设置合计样式
		CellStyle style1 = workbook.createCellStyle();
		style1.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
		style1.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
		style1.setBorderTop((short) 1);
		style1.setBorderBottom((short) 1);
		style1.setBorderLeft((short) 1);
		style1.setBorderRight((short) 1);

		Sheet sheet = workbook.createSheet(headName);
		// 2:合并单元格,表头。并设置值
		CellRangeAddress cra = new CellRangeAddress(0, 0, 0, tableHead.size() - 1);
		sheet.addMergedRegion(cra);
		Row row = sheet.createRow(0);
		Cell tableName = row.createCell(0);
		tableName.setCellStyle(style);
		tableName.setCellValue(headName);

		// 3:设置表head
		Row row1 = sheet.createRow(1);
		for (int i = 0; i < tableHead.size(); i++) {
			Cell createCell = row1.createCell(i);
			createCell.setCellValue(tableHead.get(i));
			createCell.setCellStyle(style);
		}
		// 4:表格内容
		for (int i = 0; i < tableBody.size(); i++) {
			Row rows = sheet.createRow(i + 2);
			int j = 0;
			for (Map.Entry entry : tableBody.get(i).entrySet()) {
				Cell createCell = rows.createCell(j);
				if (entry.getValue() != null && !"".equals(entry.getValue())) {
					createCell.setCellValue(entry.getValue().toString());
				} else {
					createCell.setCellValue("");
				}
				createCell.setCellStyle(style1);
				j++;
			}
		}
		// 5:设置头
		response.setHeader("Content-disposition",
				"attachment; filename=" + new String(headName.getBytes("GB2312"), "ISO8859-1") + ".xls");
		// 6:设置头类型
		response.setContentType("application/vnd.ms-excel");
		// 7:写出
		OutputStream toClient = response.getOutputStream();
		workbook.write(toClient);
		toClient.flush();
		toClient.close();

	}

	/**
	 * 下载excel文件,内容使用MAP存放
	 * 
	 * @param response
	 *            响应流
	 * @param headName
	 *            Excel文件名
	 * @param tableHead
	 *            表的title
	 * @param tableBody
	 *            表内容
	 * @throws IOException
	 */
	public static void downloadExcelObj(HttpServletResponse response, String headName, List tableHead,
			List tableBody) throws Exception {
		// 将对象转换成map
		List> mapBody = new ArrayList>();
		for (int i = 0; i < tableBody.size(); i++) {
			Map objectMap = new LinkedHashMap();
			Field[] fields = tableBody.get(i).getClass().getDeclaredFields();
			for (Field v : fields) {
				v.setAccessible(true);
				Object va = v.get(tableBody.get(i));
				if (va == null) {
					va = "";
				}
				objectMap.put(v.getName(), va);
			}
			mapBody.add(objectMap);
		}
		downloadExcelMap(response, headName, tableHead, mapBody);
	}

	/**
	 * 替换特殊字符
	 * 
	 * @param str
	 * @return
	 */
	private static String replaceAllSpecial(String str) {
		String regEx = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
		Pattern p = Pattern.compile(regEx);
		Matcher m = p.matcher(str);
		return m.replaceAll("").trim();
	}
}

4、Excel基于jxls模板导出参考代码

/**
 * Excel 模板下载
 * 
 * @author http://blog.csdn.net/make_a_difference
 */
public class ExcelTempletExport {

	/**
	 * 通过模板下载Excel文件
	 *  ${bean.courseName} 
	 * 
	 * @param templateUrl
	 *            模板路径 /org/gtiles/components/gtclasses/workbench/
	 *            teacherfacecoursecount/list/templateTeacherCourse.xlsx
	 * @param tableBody
	 *            输出内容list
	 * @param fileName
	 *            文件名称
	 * @param response
	 *            相应流
	 * 
	 * @throws Exception
	 */
	public static void downloadExcel(String templatePath, List tableBody, String fileName,
			HttpServletResponse response) throws Exception {
		Map beanParams = new HashMap();
		beanParams.put("list", tableBody);
		downloadExcel(templatePath, beanParams, fileName, response);
	}

	/**
	 * 通过模板下载Excel文件
	 *  ${bean.courseName} 
	 * 
	 * @param templateUrl
	 *            模板路径 /org/gtiles/components/gtclasses/workbench/
	 *            teacherfacecoursecount/list/templateTeacherCourse.xlsx
	 * @param map
	 *            输出内容 key-value
	 * @param fileName
	 *            文件名称
	 * @param response
	 *            相应流
	 * 
	 * @throws Exception
	 */
	public static void downloadExcel(String templatePath, Map map, String fileName,
			HttpServletResponse response) throws Exception {
		// 1: 创建XLSTransformer对象
		XLSTransformer transformer = new XLSTransformer();
		// 2:获取模板,读取jar文件并返回流
		InputStream is = ExcelTempletExport.class.getResourceAsStream(templatePath);
		try {
			// 4:设置响应头
			response.setHeader("Content-Disposition",
					"attachment;filename=\"" + new String(fileName.getBytes("gb2312"), "iso8859-1") + ".xlsx\"");
			response.setContentType("application/vnd.ms-excel");
			// 5:通过流向客户端写数据
			transformer.transformXLS(is, map).write(response.getOutputStream());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (is != null)
				is.close();
			response.getOutputStream().flush();
			response.getOutputStream().close();
		}
	}

	/**
	 * 通过模板将Excel写入到另外一个Excel文件中
	 * 
	 * @param srcFilePath
	 *            模板路径
	 * @param tableBody
	 *            输出内容
	 * @param destFilePath
	 *            输出目标文件
	 * @throws Exception
	 */
	public static void downloadExcel(String srcFilePath, List tableBody, String destFilePath) throws Exception {
		// 1: 创建XLSTransformer对象
		XLSTransformer transformer = new XLSTransformer();
		// 2:将数据转换成Map格式
		Map beanParams = new HashMap();
		beanParams.put("list", tableBody);
		// 3:写出文件
		transformer.transformXLS(srcFilePath, beanParams, destFilePath);
	}

}

5、Test 测试导入 

/**
 *  test
 * @author http://blog.csdn.net/make_a_difference
 */
public class TestBean {

	@ExcelFieldMeta(cell = 1)
	private String name;
	@ExcelFieldMeta(cell = 2, isNotNull = false)
	private Integer age;
	@ExcelFieldMeta(cell = 3)
	private Date birthday;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}
	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public static void main(String[] args) throws FileNotFoundException {
		try {
			// List excelParsing = ExcelUtil.excelParsing(new File("C:\\Users\\Administrator\\Desktop\\test.xlsx"), 1, TestBean.class);
			//1:导入 
			List excelParsing = ExcelUtil.excelParsing(new FileInputStream("C:\\Users\\Administrator\\Desktop\\test.xlsx"), 1, 1, TestBean.class);
			//2:导出
			ExcelTempletExport.downloadExcel("模板路径",excelParsing, "文件名", response);
		} catch (ExcelException e) {
			System.out.println(e.toString());//excel解析的错误。例:[2]行-[2]列:此单元格内容只能为数值,读取时为:qx
		}
	}

6、Excel导出效果,以及导出模板定义

Java POI利用反射导入导出Excel、Jxls实现Excel模板导出_第1张图片

Excel使用jxls模板导出

Java POI利用反射导入导出Excel、Jxls实现Excel模板导出_第2张图片

 

你可能感兴趣的:(Java)