每当我们尝试导出巨大的Excel电子表格(例如:大约200,000-300,000条记录)时,大多数情况下,我们最终都会遇到OutOfMemoryError:JavaHeapSpace。我们还花费更多的时间和处理能力来导出大量数据。造成这种问题的主要原因是,Apache POI的先前版本(3.8之前的版本)无法为这种情况提供适当的解决方案,并且这些版本中的API设计也存在其他问题。我有时遇到过这样的情况,在使用早期版本的POI的excel导出期间,我们不能支持超过65000行的数据。但是在3.8版或更高版本中,有针对所有这些问题的解决方案。
为了解决Excel导出的内存问题和性能问题,他们利用流API进行设计以支持大数据导出。使用流API,我们只能将几行数据刷新到内存中,而重新读取的行则可以刷新到硬内存(永久内存)中。在此示例中,您可以轻松地看到它如何支持更大的数据集。我写了这个实用程序,用我的一个应用程序支持将近200,000条记录。我希望它将对寻求这种解决方案的许多人有所帮助。我用Spring MVC构建了这个解决方案。
为了解决这个问题,我应用了模板设计模式来创建一个用于excel导出任何数据的实用程序。这是一个通用实现,您可以在任何需要的地方将其与相应实现一起使用。下面有第一个抽象类,它是泛型类。需要扩展它以实现我们相应模块的导出功能。
import java.util.List; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; /** * @author Shidram * * @param <E> */ public abstract class ExcelExportUtility< E extends Object > { protected SXSSFWorkbook wb; protected Sheet sh; protected static final String EMPTY_VALUE = " "; /** * This method demonstrates how to Auto resize Excel column */ private void autoResizeColumns(int listSize) { for (int colIndex = 0; colIndex < listSize; colIndex++) { sh.autoSizeColumn(colIndex); } } /** * * This method will return Style of Header Cell * * @return */ protected CellStyle getHeaderStyle() { CellStyle style = wb.createCellStyle(); style.setFillForegroundColor(IndexedColors.ORANGE.getIndex()); style.setFillPattern(CellStyle.SOLID_FOREGROUND); style.setBorderBottom(CellStyle.BORDER_THIN); style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); style.setBorderLeft(CellStyle.BORDER_THIN); style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); style.setBorderRight(CellStyle.BORDER_THIN); style.setRightBorderColor(IndexedColors.BLACK.getIndex()); style.setBorderTop(CellStyle.BORDER_THIN); style.setTopBorderColor(IndexedColors.BLACK.getIndex()); style.setAlignment(CellStyle.ALIGN_CENTER); return style; } /** * * This method will return style for Normal Cell * * @return */ protected CellStyle getNormalStyle() { CellStyle style = wb.createCellStyle(); style.setBorderBottom(CellStyle.BORDER_THIN); style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); style.setBorderLeft(CellStyle.BORDER_THIN); style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); style.setBorderRight(CellStyle.BORDER_THIN); style.setRightBorderColor(IndexedColors.BLACK.getIndex()); style.setBorderTop(CellStyle.BORDER_THIN); style.setTopBorderColor(IndexedColors.BLACK.getIndex()); style.setAlignment(CellStyle.ALIGN_CENTER); return style; } /** * @param columns */ private void fillHeader(String[] columns) { wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk sh = wb.createSheet("Validated Data"); CellStyle headerStle = getHeaderStyle(); for (int rownum = 0; rownum < 1; rownum++) { Row row = sh.createRow(rownum); for (int cellnum = 0; cellnum < columns.length; cellnum++) { Cell cell = row.createCell(cellnum); cell.setCellValue(columns[cellnum]); cell.setCellStyle(headerStle); } } } /** * @param columns * @param dataList * @return */ public final SXSSFWorkbook exportExcel(String[] columns, List<E> dataList) { fillHeader(columns); fillData(dataList); autoResizeColumns(columns.length); return wb; } /** * @param dataList */ abstract void fillData(List<E> dataList); }
通过扩展上面的类,我们可以实现我们自己的excel实用程序来导出数据。在此扩展类中,我们必须重写'fillData()'方法以提供要导出的数据。例如,我在下面的一个此类课程中进行了演示:
import java.text.SimpleDateFormat; import java.util.List; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; /** * @author Shidram * */ public class ExportRevisionResponseExcel extends ExcelExportUtility<RevisionResponse> { /* * @see ASTExcelExportUtility#fillData(java.util.List) */ void fillData(List<RevisionResponse> dataList) { CellStyle normalStyle = getNormalStyle(); int rownum = 1; SimpleDateFormat dtFormat = new SimpleDateFormat("E MMM dd HH:mm:ss z yyyy"); for (RevisionResponse rev : dataList) { Row row = sh.createRow(rownum); Cell cell_0 = row.createCell(0, Cell.CELL_TYPE_STRING); cell_0.setCellStyle(normalStyle); cell_0.setCellValue(rev.getRevId()); Cell cell_1 = row.createCell(1, Cell.CELL_TYPE_STRING); cell_1.setCellStyle(normalStyle); cell_1.setCellValue(rev.getJcrCreated() != null ? dtFormat.format(rev.getJcrCreated()) : " "); rownum++; } } }
现在该实用程序已准备就绪。下一步是从某些Action或Controller代码中调用此实用程序以导出数据。在这种情况下,我将提供Spring控制器的Method代码。为了便于理解,我仅提供控制器所需的代码段。对于要导出的数据,我正在使用ServletContext来获取已经可用的搜索数据,以避免对业务方法造成多次打击。因此,我使用SeverletContext从搜索方法中输入数据,并从同一控制器的Export方法中获取相同数据。这是来自控制器的代码:
@Controller public class RevisionResponseController { ...... @Autowired private ServletContext servletContext; ...... @SuppressWarnings("unchecked") @RequestMapping(value = "/export", method = RequestMethod.GET) public ModelAndView exportRevisionsToExcel(ModelAndView modelAndView) { List<RevisionResponse> revList = (List<RevisionResponse>) servletContext.getAttribute("revisionsResponse"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_hh_mm_ss"); String excelFileName = "Revisions_" + formatter.format(LocalDateTime.now()) + ".xlsx"; SXSSFWorkbook wb = (new ExportRevisionResponseExcel()).exportExcel(new String[] { "REVISION ID", "CREATION DATE" }, revList); try { ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); wb.write(outByteStream); byte[] outArray = outByteStream.toByteArray(); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setContentLength(outArray.length); response.setHeader("Expires:", "0"); // eliminates browser caching response.setHeader("Content-Disposition", "attachment; filename=" + excelFileName); OutputStream outStream = response.getOutputStream(); outStream.write(outArray); outStream.flush(); wb.dispose(); wb.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return modelAndView; } ...... ...... }
就这样。该功能现已准备就绪。下一步是从UI操作中调用此控制器方法。
如果您愿意,请发表您的评论。如果您认为自己有比这更好的方法,还可以提供您的建议。我非常感谢这些建议。
原文链接:http://codingdict.com