导出excel模板,由Jxls技术换为POI,以及解决POI与Jxls的jar包冲突问题

项目里有一个功能,是将数据库查询出的数据全部导出为excel报表,由于数据复杂,使用的是jxls模板,就是这样:


jxls代码是这样的:

//      获得输出的响应
	HttpServletResponse response = this.getResponse();
	HSSFWorkbook workbook = JxlsManager.getWorkBookByTemplate(
		"template.xls", dataList, "dataVo");
	JxlsManager.exportExcel(workbook, response, "导出数据清单");

JxlsManager为自己写的工具类,此处用到的方法内容如下

//从一个Excel模板中得到一个WORKBOOK
	public static HSSFWorkbook getWorkBookByTemplate(String templateFileName,
			Object exportObj, String objectName) throws Exception {
		//获取Excel模板的全路径(此处根据实际情况替换为拼成路径的方法)
		templateFileName = new JxlsManager().getPath()+templateFileName;
		templateFileName = "/" + templateFileName;
		Map beans = new HashMap();
		beans.put(objectName, exportObj);
		XLSTransformer transformer = new XLSTransformer();
		InputStream is = new BufferedInputStream(new FileInputStream(
				templateFileName));
		HSSFWorkbook workbook = transformer.transformXLS(is, beans);
		return workbook;

	}
//此方法用来将workbook输出到页面上,提供给用户下载
        public static void exportExcel(HSSFWorkbook workBook, HttpServletResponse response, String excelName) throws Exception {
		//输出文件的格式
		response.setContentType("application/vnd.ms-excel");
		OutputStream os = response.getOutputStream();
		//处理名字为中文的问题
		excelName = new String(excelName.getBytes("GB18030"),"ISO8859-1");
		//设置导出文件的名字
		response.addHeader("Content-Disposition", "attachment; filename=\""+excelName+".xls\"");   
		workBook.write(os);
		os.flush();
		os.close();
	}

Jxls可以非常灵活地读取Excel模板的EL表达式,所以非常方便,但是速度很慢,所以作者接到了这个需求,将Jxls技术换成效率很高的POI。

那么问题来了,如何使用POI来读取模板中的EL表达式呢,我的第一反应是使用反射,代码如下:

/**
 * 功能:完成POI部分的公用功能 
 */
public class POIManager {

	/**
	 * 从一个样例中得到一个WORKBOOK
	 * 
	 * @param templateFileName
	 * @return
	 * @throws Exception
	 */
	public static HSSFWorkbook getHSSFWorkBookByTemplate(String templateFileName) throws Exception {
		File template = new File("/" + new POIManager().getPath() + templateFileName);
		FileInputStream fis;
		fis = new FileInputStream(template);
		HSSFWorkbook wb = new HSSFWorkbook(fis);
		fis.close();
		return wb;
	}

	/** 获得模板路径 */
	public String getPath() {
		URL url = getClass().getClassLoader().getResource("/");
		String strFilePath = "";
		if (strFilePath != null) {// 保证不为空
			strFilePath += url.toString();
			if (strFilePath.startsWith("file:") && strFilePath.length() > 6) {// 保证windows环境
				strFilePath = strFilePath.substring(6);
			}
			if (strFilePath.indexOf("WEB-INF/classes") > -1) {
				strFilePath = strFilePath.substring(0, strFilePath.indexOf("WEB-INF/classes"));
			}
		}
		return strFilePath += "common/xls/export/";
	}

	/**
	 * 读取模板中的EL表达式,获得所有需要反射执行的Method,存入Map
	 * @param templateFileName
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes" })
	public Map<Integer, Method> getMethodsMap(String templateFileName) throws Exception {
		File template = new File("/" + getPath() + templateFileName);

		// 读取模板中定义el表达式的一行
		List methods = ExcelImportUtil.readExcelRow(template, 3);

		// 获取所有的需要反射执行的Method方法顺序放入map
		Map<Integer, Method> map = new HashMap<Integer, Method>();
		for (int i = 0; i < methods.size(); i++) {
			String methodName = (String) methods.get(i);
			// 将methodName从${body.xxx}变为getXxx
			if (methodName != "" && methodName != null) {
				String[] split = methodName.split("\\.");
				methodName = split[1].replace('}', ' ').trim();
				String upperCase = methodName.substring(0, 1).toUpperCase();
				methodName = upperCase + methodName.substring(1);
				methodName = "get" + methodName;
				Class<?> clazz = null;
				if(templateFileName=="prpCcargoPolicyVos.xls"){
					clazz = PrpCcargoPolicyVo.class;
				}
				else if(templateFileName=="aviationPrpCPolicy.xls"){
					clazz = PrpCcargoPolicyVo.class;
				}
				else{
					clazz = PrpCpolicyVo.class;
				}
				Method method = clazz.getMethod(methodName);
				map.put(i, method);
			} else {
				continue;
			}
		}
		return map;
	}

	/**
	 * 获得cellstyle
	 * 
	 * @param wb
	 * @return
	 */
	public static CellStyle getCellStyle(HSSFWorkbook wb) {
		CellStyle style = wb.createCellStyle();
		Font font = wb.createFont();
		font.setFontName("宋体");
		font.setFontHeightInPoints((short) 10);
		style.setFont(font);
		// style1.setFillBackgroundColor(HSSFColor.LIGHT_YELLOW);
		style.setAlignment(CellStyle.ALIGN_CENTER); // 横向居中
		style.setVerticalAlignment(CellStyle.VERTICAL_CENTER); // 纵向居中
		style.setBorderTop(CellStyle.BORDER_THIN); // 上细线
		style.setBorderBottom(CellStyle.BORDER_THIN); // 下细线
		style.setBorderLeft(CellStyle.BORDER_THIN); // 左细线
		style.setBorderRight(CellStyle.BORDER_THIN);
		return style;
	}

	/**
	 * 输出Excel
	 * @param response
	 * @param wb
	 * @param dataList
	 * @param methodsMap
	 * @throws Exception
	 */
	public static void exportExcel(HttpServletResponse response, HSSFWorkbook wb, List dataList,
			Map<Integer, Method> methodsMap,String excelName) throws Exception {
		// 获取第一页sheet
		HSSFSheet sheet = wb.getSheetAt(0);
		//获取cellStyle
		CellStyle cellStyle = getCellStyle(wb);
		for (int i = 0; i < dataList.size(); i++) {
			//===============================================
			Object pr = null;
			pr =  (DataVo) dataList.get(i);
			//===============================================
			HSSFRow row = sheet.createRow(i + 1);
			row.setHeightInPoints(36);
			for (int j = 0; j < methodsMap.size(); j++) {
				HSSFCell cell = row.createCell(j);
				cell.setCellStyle(cellStyle);
				Method method = methodsMap.get(j);
				if (method == null) {
					continue;
				}
				Object invoke = method.invoke(pr);
				// 判断方法返回值类型,对返回值invoke类型强转
				Class<?> returnType = method.getReturnType();
				if (invoke == null) {
					cell.setCellValue("");
					continue;
				}
				if (returnType == String.class) {
					if ((String) invoke == "") {
						cell.setCellValue("");
					} else {
						String str = (String) invoke;
						str = str.trim();
						cell.setCellValue(str);
					}
				}
				if (returnType == Double.class) {
					cell.setCellValue((Double) invoke);
				}
				if (returnType == Integer.class) {
					cell.setCellValue((Integer) invoke);
				}
				if (returnType == Date.class) {
					Date dateTemp = (Date) invoke;
					cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(dateTemp).toString());
				}
			}
		}

		response.setContentType("application/vnd.ms-excel");
		OutputStream os = response.getOutputStream();
		// 处理名字为中文的问题
		excelName = new String(excelName.getBytes("GB18030"), "ISO8859-1");
		// 设置导出文件的名字
		response.addHeader("Content-Disposition", "attachment; filename=\"" + excelName + ".xls\"");
		wb.write(os);
		os.flush();
		os.close();
	}
}

这样就初步完成了POI读取模板中EL表达式完成导出的情况,因为是反射,所以速度肯定会慢一些,我想应该还有比反射更好的手段,只不过目前已经比Jxls速率快多了,我在本地测试,3w条数据,Jxls大概要190s,而POI只需要2-5s,简直是碾压的差距。

不过相比Jxls,POI有个致命的弱点就是不能实现读取EL表达式,如果模板复杂,像下面这样,POI读模板就不好实现,需要重写方法,我的决定是分情况决定技术,简单模板使用poi提升速度,复杂模板就使用jxls。


这样的话,就需要项目中同时支持POI与Jxls两种技术。但是问题来了,原本项目中使用的是poi-3.2的jar包来支持jxls-0.9.8,我为了使用POI,导入了poi-3.5的jar包,引入poi-3.5就必然要删除poi-3.2,但是poi-3.5的jar包无法支持jxls中这个方法

transformer.transformXLS(is, beans);

所以要么使用jxls,速度慢,要么使用POI,没有jxls灵活,无法实现完全自由的读模板,一旦遇到复杂模板就很难处理。

那么有没有什么方法能够让两个不同版本poi的jar包共存呢,有,那就是自定义jar包!

冲突的原因就是因为不同版本的jar包,其中的类路径和方法名都相同,那么我们可以将新引入的poi-3.5的包名修改为orgX.apache...(随便怎么改都行)

在百度之后,我发现了一个叫做jarjar.jar的工具jar包,可以修改jar包的包名。大家可以去百度使用方法,这里就不赘述了。

【这里有一个小插曲,jarjar.jar这个包网上提供下载的不多,推荐大家去http://www.java2s.com/这个网站下载,jar包和其他编程资源比较全。】

将jar包修改过之后,命名为poiX-3.5.jar

在JxlsManager中导入jar包不变,而POIManager中导入jar包路径为orgX.apache,这样就不会冲突了,如下所示

import orgX.apache.poi.hssf.usermodel.HSSFCell;
import orgX.apache.poi.hssf.usermodel.HSSFRow;
import orgX.apache.poi.hssf.usermodel.HSSFSheet;
import orgX.apache.poi.hssf.usermodel.HSSFWorkbook;
import orgX.apache.poi.ss.usermodel.CellStyle;
import orgX.apache.poi.ss.usermodel.Font;

用哪个导哪个,亲测有效。

其实自定义jar包根本不难,难的是很多时候思维被jar包束缚住,没想到可以改jar包,所以编程既需要技术,更需要思维的开阔啊/捂脸。


  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值