最开始调用的方法是(标记的地方): workbook = new HSSFWorkbook();和 workbook = new XSSFWorkbook();
这两个方法就是导出Excel的最关键的方法,接下来我来说说这两个方法作用:
1.HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls;
2.XSSFWorkbook:是操作Excel2007的版本,扩展名是.xlsx;
对于不同版本的EXCEL文档要使用不同的工具类,如果使用错了,会提示如下错误信息。
org.apache.poi.openxml4j.exceptions.InvalidOperationException
org.apache.poi.poifs.filesystem.OfficeXmlFileException
当数据量超出65536条后,在使用HSSFWorkbook或XSSFWorkbook,程序会报OutOfMemoryError:Javaheap space;内存溢出错误。
而我们的数据量达到了9W条数据,用这两个方法肯定是报内存溢出的错误的。
最终我找到的解决方法是:从POI 3.8版本开始,提供了一种基于XSSF的低内存占用的API----SXSSFWorkbook。
3.SXSSFWorkbook-来至官方的解释:实现“BigGridDemo”策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。您可以提供用作书面数据基础的模板工作簿。有关详细信息,请参见https://poi.apache.org/spreadsheet/how-to.html#sxssf。请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释......仍然只存储在内存中,因此如果广泛使用,可能需要大量内存。SXSSFWorkbook默认使用内联字符串而不是共享字符串表。这非常有效,因为没有文档内容需要保存在内存中,但也被称为制作与某些客户不兼容的文档。在启用共享字符串的情况下,文档中的所有唯一字符串必须保存在内存中。根据您的文档内容,这可能比共享字符串被禁用时使用更多的资源。在决定是否启用共享字符串之前,请仔细检查您的内存预算和兼容性需求。
而在poi架包中提供的方法用的是:
当数据量超过1000条时就会调用SXSSFWorkbook方法,从而解决了海量的数据导出会发生内存溢出的问题。
public void exportContacts(final PortalUserPage page, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final String name = "通讯录列表"; // 去掉分页 page.setPage(1); page.setRows(Integer.MAX_VALUE); final List<CmssPortalUser> dataList = (List<CmssPortalUser>) this.queryPortalUserListWithGroup(page); final List<PortalUserExcelDto> userList = new ArrayList<>(); for (final CmssPortalUser user : dataList) { final PortalUserExcelDto userDto = new PortalUserExcelDto(); userDto.setIndex(user.getIndex() != null ? user.getIndex().toString() : "0"); userDto.setRealName(user.getRealName()); userDto.setCompany(user.getCompany()); userDto.setCompanyBranch(user.getCompanyBranch()); userDto.setDepartment(user.getDepartment()); userDto.setTitle(user.getTitle()); userDto.setPhone(user.getPhone()); userDto.setLoginName(user.getLoginName()); userDto.setProducts(user.getProducts()); userDto.setEmail(user.getEmail()); userDto.setGroup(user.getGroup()); if (user.getOpenTime() != null) { // 开通时间日期格式化 final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); userDto.setOpenTime(format.format(user.getOpenTime())); } // 判断全局预览 // 根据创建者ID查询该用户是否可以浏览全局 user.setIfGlobelView(shareJedis.ifGlobelView(user.getId().longValue()) ? 1 : 0); if (user.getIfGlobelView() != null) { if (user.getIfGlobelView() == Constants.CURRENCY.ZERO) { userDto.setIfGlobelView("否"); } if (user.getIfGlobelView() == Constants.CURRENCY.ONE) { userDto.setIfGlobelView("是"); } } // 判断状态 if (StringUtils.isNotBlank(user.getStatus())) { if (Constants.CONTACTS_STATUS.UNAVAILABILITY.equals(user.getStatus())) { userDto.setStatus("停用"); } if (Constants.CONTACTS_STATUS.AVAILABILITY.equals(user.getStatus())) { userDto.setStatus("启用"); } } // VIP类别 userDto.setVipCategory(user.getVipCategoryName() != null ? user.getVipCategoryName() : null); // Vip级别 userDto.setVipLevel(user.getVipLevelName() != null ? user.getVipLevelName() : null); // 判断短信通知 if (user.getIsSendSms() != null) { if (Constants.UNIVERSAL_CONSTANT.ZERO.equals(user.getIsSendSms())) { userDto.setIsSendSms("否"); } if (Constants.UNIVERSAL_CONSTANT.ONE.equals(user.getIsSendSms())) { userDto.setIsSendSms("是"); } } userList.add(userDto); } final ExportParams params = new ExportParams(name, name, ExcelType.XSSF); final Workbook workbook = ExcelExportUtil.exportExcel(params, PortalUserExcelDto.class, userList); try { final String agent = request.getHeader("User-Agent").toLowerCase(); // 开始下载文件 final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); String filename = format.format(new Date().getTime()) + "-" + name + ".xlsx"; if (!StringUtils.isEmpty(agent) && agent.contains("msie") || agent.contains("like gecko") || (agent.indexOf("rv") > 0 && agent.indexOf("firefox") == -1)) { filename = URLEncoder.encode(filename, "UTF-8"); } else { filename = new String(filename.getBytes("UTF-8"), "ISO882023年06月12日"); } response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=".concat(filename)); final OutputStream out = response.getOutputStream();
//写内容,xls文件已经可以打开 workbook.write(out); out.flush(); } catch (IOException e) { log.error(e.getMessage()); } finally {
try {
out.close();
//删除之前保存的临时文件
wb.dispose();
} catch (Exception ex){
logger.error("export io exception");
}
} }
二、SXSSFWorkbook:低内存占用的工作簿
引用官方的介绍,简单概括就是:
SXSSF是对XSSF的一种流式扩展,特点和原理是采用了滑动窗口的机制,低内存占用,主要用于数据量非常大的电子表格而虚拟机堆有限的情况。
SXSSFWorkbook.DEFAULT_WINDOW_SIZE
默认值是100,表示在内存中最多存在100个Row对象,当写第101个Row对象的时候就会把第1个Row对象以XML格式写入C:\Users\wange\AppData\Local\Temp路径下的临时文件中,后面的以此类推,始终保持内存中最多存在100个Row对象。
SXSSFWorkbook默认使用内联字符串而不是共享字符串表(SharedStringsTable)。启用共享字符串时,文档中的所有唯一字符串都必须保存在内存中,因此XSSF会占用更多的内存。
与XSSF的对比,在一个时间点上,只可以访问一定数量的Row;不再支持Sheet.clone();不再支持公式的求值。但是除了滑动窗口,其余的EXCLE操作仍然使用的是XSSF的API。
官方提示导出EXCEL后应该调用wb.dispose()
来删除之前保存的临时文件。
三、SXSSFWorkbook导出Excel示例
通过poi导出excel的过程大致是这样的:
- 导入POI的jar包,使用对应的POI对象(本篇选择SXSSFWorkbook)
- 创建 sheet 表
- 创建 row 行
- 创建 cell 每行的单元格(可设置数据的格式,和单元格的格式)
- 也可在所有数据写好之后,整体对某列某行来设置格式
- 通过IO流输出
SXSSFWorkbook的使用,与HSSFWorkbook基本一致,只需要换掉对应的Sheet、Row、Cell等类就可以了;
sheet,row,cell在建立的时候Index都是从0开始的;
四、SXSSF单元的样式设置,标红加粗之类
// 设置单元格各种样式 // 设置字体 Font font = wb.createFont(); //字体高度 font.setFontHeightInPoints((short) 11); //字体颜色 font.setColor(Font.COLOR_NORMAL); //字体 font.setFontName("宋体"); //设置单元格里的字体样式,使用上面设置对字体样式,以及设置单元格的格式 CellStyle cellStyle = wb.createCellStyle(); cellStyle.setFont(font); //水平布局:居中 cellStyle.setAlignment(CellStyle.ALIGN_CENTER); //单元格垂直居中 cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); //换行 cellStyle.setWrapText(true);
五、对于身份证等长数字数据,设置CELL单元格为文本格式
/** *设置CELL格式为文本格式 */ CellStyle cellStyle = wb.createCellStyle(); DataFormat format = wb.createDataFormat(); //"@"是指文本的数据格式,主要是这段代码 cellStyle.setDataFormat(format.getFormat("@")); cell.setCellStyle(cellStyle); cell.setCellValue(你的值); //CELL_TYPE_STRING是单元格格式,这里写不写没区别…… 原因如下解释 cell.setCellType(Cell.CELL_TYPE_STRING);
标签:Java,Excel,SXSSFWorkbook,user,new,userDto,final,内存 From: https://www.cnblogs.com/min225016/p/17477077.html