java POI导出excel性能优化

项目场景:

导出40万以上的数据到xlsx

问题描述:

实施地出现导出40万数据到excel中时,CPU占不超过100%,时间超过半个小时,系统中其他功能模块卡顿
	@EcpGetMapping("/exportResultDetail")
	public void exportResultDetail(final HttpServletRequest req, final HttpServletResponse resp, @RequestParam final String operParams) {
		byte[] exportBytes = null;
		ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        try {
        	JSONObject jsonObj = JSONObject.parseObject(operParams);
        	String fileName = String.valueOf(jsonObj.get("fileName"));
        	String fileType = String.valueOf(jsonObj.get("fileType"));
        	String compId = String.valueOf(jsonObj.get("compId"));
        	String modelId = String.valueOf(jsonObj.get("modelId"));
        	List<Map> colList = this.mainResultService
        								.findColListByModelId(compId, modelId);
        	String queryParamStr = String.valueOf(jsonObj.get("queryParam"));
        	Map queryParam = JSONUtil.fromJsonString(queryParamStr, Map.class);
//        	fileName = new String(fileName.getBytes("iso8859-1"),"utf8");
        	Map<String, Object> queryResult = this.tAmModelMainResultService
        									.queryDetailList(queryParam);
        	List<Map> list = (List<Map>) queryResult.get("data");
    		final Workbook exportWorkbook = AmExcelHelpUtil.createWorkbook(fileType);
    		exportWorkbook.createSheet(fileName);
    		final CellStyle headCellStyle = exportWorkbook.createCellStyle();
    		final CellStyle contentCellStyle = exportWorkbook.createCellStyle();
    		final CellStyle contentCenterCellStyle = exportWorkbook.createCellStyle();
    		final CellStyle contentRightCellStyle = exportWorkbook.createCellStyle();
    		final Font headCellFont = exportWorkbook.createFont();
    		final Font contentFont = exportWorkbook.createFont();
    		final Sheet manuSheetNew = exportWorkbook.getSheetAt(0);
    		manuSheetNew.setDefaultRowHeightInPoints((short) 25);
    		manuSheetNew.setDefaultColumnWidth(25);
    		final Row headRow = manuSheetNew.createRow(0); // 标题行
    		int colCount = colList.size();
    		for (int j = 0; j < colCount; j++) {
    			final Cell cell = headRow.createCell(j);
    			headCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
    			headCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
    			headCellStyle.setWrapText(true);
    			headCellFont.setFontHeightInPoints((short)10);
    			headCellFont.setFontName("微软雅黑");
    			headCellFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
    			headCellStyle.setFont(headCellFont);
    			headCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
    			headCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
    			headCellStyle.setBorderRight(CellStyle.BORDER_THIN);
    			headCellStyle.setBorderTop(CellStyle.BORDER_THIN);
    			headCellStyle.setFillForegroundColor(HSSFColor.LIGHT_CORNFLOWER_BLUE.index);
    			headCellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
    			cell.setCellStyle(headCellStyle);
    			Map colMap = colList.get(j);
    			String colCnName = String.valueOf(colMap.get("colcnname"));
    			excelHelpUtil.setCellValue(fileType, manuSheetNew, 0, j, colCnName);
    		}
    		if (list != null && list.size() > 0) {
    			int indx = 1;
    			for (Map dataMap : list) {
    				if (dataMap == null) {
    					continue;
    				}
    				final Row row = manuSheetNew.createRow(indx);
        			row.setHeightInPoints((short) 25);
        			for (int j = 0; j < colCount; j++) {
        				final Cell cell = row.createCell(j);
        				Map colMap = colList.get(j);
        				String colEnName = String.valueOf(colMap.get("colenname")).toLowerCase();
        				String dataType = String.valueOf(colMap.get("coldatatype"));
        				if ("1,2".contains(dataType)) {
        					contentCenterCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
        					contentCenterCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        					contentCenterCellStyle.setWrapText(true);
            				contentFont.setFontHeightInPoints((short) 10);
            				contentFont.setFontName("微软雅黑");
            				contentCenterCellStyle.setFont(contentFont);
            				contentCenterCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
            				contentCenterCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
            				contentCenterCellStyle.setBorderRight(CellStyle.BORDER_THIN);
            				contentCenterCellStyle.setBorderTop(CellStyle.BORDER_THIN);
            				cell.setCellStyle(contentCenterCellStyle);
        				} else if ("3".equals(dataType)) {
        					contentRightCellStyle.setAlignment(CellStyle.ALIGN_RIGHT);
        					contentRightCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        					contentRightCellStyle.setWrapText(true);
            				contentFont.setFontHeightInPoints((short) 10);
            				contentFont.setFontName("微软雅黑");
            				contentRightCellStyle.setFont(contentFont);
            				contentRightCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
            				contentRightCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
            				contentRightCellStyle.setBorderRight(CellStyle.BORDER_THIN);
            				contentRightCellStyle.setBorderTop(CellStyle.BORDER_THIN);
            				cell.setCellStyle(contentRightCellStyle);
        				} else {
        					contentCellStyle.setAlignment(CellStyle.ALIGN_LEFT);
            				contentCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
            				contentCellStyle.setWrapText(true);
            				contentFont.setFontHeightInPoints((short) 10);
            				contentFont.setFontName("微软雅黑");
            				contentCellStyle.setFont(contentFont);
            				contentCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
            				contentCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
            				contentCellStyle.setBorderRight(CellStyle.BORDER_THIN);
            				contentCellStyle.setBorderTop(CellStyle.BORDER_THIN);
            				cell.setCellStyle(contentCellStyle);
        				}
        				excelHelpUtil.setCellValue(fileType, 
            					manuSheetNew, indx, j, String.valueOf(dataMap.get(colEnName)));
        			}
        			indx++;
    			}
    		}
    		exportWorkbook.write(byteArrayOut);
    		exportBytes = byteArrayOut.toByteArray();
        	resp.setStatus(200);
            resp.setContentType("application/x-download;charset=utf8");
            resp.addHeader("Content-Disposition", String.format("attachment; filename=\"%s\"",
            			URLEncoder.encode(fileName + "." + fileType, "UTF-8")));
			resp.getOutputStream().write(exportBytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

原因分析:

  1. 大数据量导出应该使用poi 3.8以上提供的类SXSSFWorkbook,2007版 XSSFWorkbook不适用于数据量较大的数据导出。
  2. 创建样式对象时,应重复使用,不要每次填充cell时都新建一个样式对象。

解决方案:

/**
	 * 
	 * @param req
	 * @param resp
	 * @param operParams
	 */
	@EcpGetMapping("/exportResultDetailForExcel")
	public void exportResultDetailForExcel(final HttpServletRequest req, final HttpServletResponse resp, @RequestParam final String operParams) {
		byte[] exportBytes = null;
		ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
		SXSSFWorkbook exportWorkbook = null;
        try {
        	JSONObject jsonObj = JSONObject.parseObject(operParams);
        	String fileName = String.valueOf(jsonObj.get("fileName"));
        	String fileType = String.valueOf(jsonObj.get("fileType"));
        	String compId = String.valueOf(jsonObj.get("compId"));
        	String modelId = String.valueOf(jsonObj.get("modelId"));
        	List<Map> colList = this.mainResultService
        								.findColListByModelId(compId, modelId);
        	String queryParamStr = String.valueOf(jsonObj.get("queryParam"));
        	Map queryParam = JSONUtil.fromJsonString(queryParamStr, Map.class);
        	Map<String, Object> queryResult = this.tAmModelMainResultService
        									.queryDetailList(queryParam);
        	List<Map> list = (List<Map>) queryResult.get("data");
        	exportWorkbook = new SXSSFWorkbook(2000);
        	exportWorkbook.setCompressTempFiles(false);
    		exportWorkbook.createSheet(fileName);
    		final CellStyle headCellStyle = exportWorkbook.createCellStyle();
    		final CellStyle contentCenterCellStyle = exportWorkbook.createCellStyle();
    		final Font headCellFont = exportWorkbook.createFont();
			headCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
			headCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
			headCellStyle.setWrapText(true);
			headCellFont.setFontHeightInPoints((short)10);
			headCellFont.setFontName("微软雅黑");
			headCellFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
			headCellStyle.setFont(headCellFont);
			headCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
			headCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
			headCellStyle.setBorderRight(CellStyle.BORDER_THIN);
			headCellStyle.setBorderTop(CellStyle.BORDER_THIN);
			headCellStyle.setFillForegroundColor(HSSFColor.LIGHT_CORNFLOWER_BLUE.index);
			headCellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
    		final Font contentFont = exportWorkbook.createFont();
    		//居中
    		contentFont.setFontHeightInPoints((short) 10);
			contentFont.setFontName("微软雅黑");
    		contentCenterCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
			contentCenterCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
			contentCenterCellStyle.setWrapText(true);
			contentCenterCellStyle.setFont(contentFont);
			contentCenterCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
			contentCenterCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
			contentCenterCellStyle.setBorderRight(CellStyle.BORDER_THIN);
			contentCenterCellStyle.setBorderTop(CellStyle.BORDER_THIN);
			//居右
    		final CellStyle contentRightCellStyle = exportWorkbook.createCellStyle();
    		contentRightCellStyle.setAlignment(CellStyle.ALIGN_RIGHT);
			contentRightCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
			contentRightCellStyle.setWrapText(true);
			contentRightCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
			contentRightCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
			contentRightCellStyle.setBorderRight(CellStyle.BORDER_THIN);
			contentRightCellStyle.setBorderTop(CellStyle.BORDER_THIN);
			contentFont.setFontHeightInPoints((short) 10);
			contentFont.setFontName("微软雅黑");
			contentRightCellStyle.setFont(contentFont);
			//居左
    		final CellStyle contentCellStyle = exportWorkbook.createCellStyle();
			contentCellStyle.setAlignment(CellStyle.ALIGN_LEFT);
			contentCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
			contentCellStyle.setWrapText(true);
			contentFont.setFontHeightInPoints((short) 10);
			contentFont.setFontName("微软雅黑");
			contentCellStyle.setFont(contentFont);
			contentCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
			contentCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
			contentCellStyle.setBorderRight(CellStyle.BORDER_THIN);
			contentCellStyle.setBorderTop(CellStyle.BORDER_THIN);
    		final Sheet manuSheetNew = exportWorkbook.getSheetAt(0);
    		manuSheetNew.setDefaultRowHeightInPoints((short) 25);
    		manuSheetNew.setDefaultColumnWidth(25);
    		final Row headRow = manuSheetNew.createRow(0); // 标题行
    		int colCount = colList.size();
    		for (int j = 0; j < colCount; j++) {
    			final Cell cell = headRow.createCell(j);   
    			cell.setCellStyle(headCellStyle);
    			Map colMap = colList.get(j);
    			String colCnName = String.valueOf(colMap.get("colcnname"));
    			excelHelpUtil.setSXSSFCellValue(fileType, manuSheetNew, 0, j, colCnName);
    		}
    		if (list != null && list.size() > 0) {
    			int indx = 1;
    			for (Map dataMap : list) {
    				if (dataMap == null) {
    					continue;
    				}
    				final Row row = manuSheetNew.createRow(indx);
        			row.setHeightInPoints((short) 25);
        			for (int j = 0; j < colCount; j++) {
        				final Cell cell = row.createCell(j);
        				Map colMap = colList.get(j);
        				String colEnName = String.valueOf(colMap.get("colenname")).toLowerCase();
        				String dataType = String.valueOf(colMap.get("coldatatype"));
        				if ("1,2".contains(dataType)) {			
            				cell.setCellStyle(contentCenterCellStyle);
        				} else if ("3".equals(dataType)) {              				
            				cell.setCellStyle(contentRightCellStyle);
        				} else {     					
            				cell.setCellStyle(contentCellStyle);
        				}
        				excelHelpUtil.setSXSSFCellValue(fileType, 
            					manuSheetNew, indx, j, String.valueOf(dataMap.get(colEnName)));
        			}
        			indx++;
    			}
    		}
    		exportWorkbook.write(byteArrayOut);
    		exportBytes = byteArrayOut.toByteArray();
        	resp.setStatus(200);
            resp.setContentType("application/x-download;charset=utf8");
            resp.addHeader("Content-Disposition", String.format("attachment; filename=\"%s\"",
            			URLEncoder.encode(fileName + "." + fileType, "UTF-8")));
			resp.getOutputStream().write(exportBytes);
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if (exportWorkbook != null) {
				try {
					//删除磁盘上的文件
					deleteSXSSFTempFiles(exportWorkbook);
				} catch (NoSuchFieldException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}
	/**
	 * Returns a private attribute of a class
	 * @param containingClass The class that contains the private attribute to retrieve
	 * @param fieldToGet The name of the attribute to get
	 * @return The private attribute
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException 
	 */
	public static Object getPrivateAttribute(final Object containingClass, final String fieldToGet) throws NoSuchFieldException, IllegalAccessException {
	    //get the field of the containingClass instance
	    Field declaredField = containingClass.getClass().getDeclaredField(fieldToGet);
	    //set it as accessible
	    declaredField.setAccessible(true);
	    //access it
	    Object get = declaredField.get(containingClass);
	    //return it!
	    return get;
	}

	/**
	 * 删除缓存文件 Deletes all temporary files of the SXSSFWorkbook instance.
	 * @param workbook
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException 
	 */
	public static void deleteSXSSFTempFiles(final SXSSFWorkbook workbook) throws NoSuchFieldException, IllegalAccessException {
	    int numberOfSheets = workbook.getNumberOfSheets();
	    //iterate through all sheets (each sheet as a temp file)
	    for (int i = 0; i < numberOfSheets; i++) {
	        Sheet sheetAt = workbook.getSheetAt(i);
	        //delete only if the sheet is written by stream
	        if (sheetAt instanceof SXSSFSheet) {
	            SheetDataWriter sdw = (SheetDataWriter) getPrivateAttribute(sheetAt,"_writer");
	            //如果exportWorkbook.setCompressTempFiles(true);
	            //下一行代码就会报错
	            File f = (File) getPrivateAttribute(sdw,"_fd");
	            try {
	                f.delete();
	            } catch (Exception ex) {
	                //could not delete the file
	            }
	        }
	    }
	}

说明:由于poi 3.8 没有 dispose()方法,所以需要写清除硬盘上存的文件,poi 3.8以上就可以直接使用。
exportWorkbook = new SXSSFWorkbook(2000);
就是将超过2000条的数据存入到电脑磁盘上,2000以内的数据放入缓存中,这样就可以减少内存的占用。创建样式的话,不需要放在for循环中,这样就解决问题了,36万的数据只需要10几秒就导出完成,提高了导出的性能。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值