Java 大数据量高性能 Excel 导出

本贴最后更新于 2323 天前,其中的信息可能已经事过景迁

本文由黑壳博客发布

本文来源 Java 大数据量高性能 Excel 导出 - 黑壳网

壳叔搞笑时间

单位开会,主任说了快一个小时了,我都睡着了,突然被主任点名叫了起来。
睡得迷迷糊糊的我,嘟囔了一句:就你有嘴,一天叭叭的!

正文

前段日子,公司的电商后台导出会员和订单数据突然把 tomcat 崩掉了。

查看异常日志

然后看了一下服务器的 tomcat 的日志,tomcat 报异常为:

java.lang.OutOfMemoryError: Java heap space

意思为内存泄露,看了下代码又问了下之前写这个的同事。

最终确定问题

导出 Excel 没做缓存处理,将导出的数据全部堆在内存里,导致内存泄露了。

解决方案

由于之前代码运行了很久,并且不能轻易修改,所以打算重写这一份 Excel 导出代码。
同样是采用 Apache-poi 插件

代码部分

用于导入 jar 文件
pom.xml 文件

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.10-FINAL</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.10-FINAL</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.10-FINAL</version> </dependency>

工具类 重点代码示例

import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigDecimal; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * Created by kzyuan on 16/12/5. */ public class ExportExcel { private static final Logger LOGGER = LoggerFactory.getLogger(ExportExcel.class); public static void generateExcel(HttpServletResponse response, Map<String, Object[]> data, String fileName, String sheetName, int[] columnWidths) throws IOException { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(sheetName); for (int i = 0; i < columnWidths.length; ++i) { sheet.setColumnWidth(i, columnWidths[i] * 256); } //Iterate over data and write to sheet Set<String> keyset = data.keySet(); int rownum = 0; for (String key : keyset) { Row row = sheet.createRow(rownum++); Object[] objArr = data.get(key); int cellnum = 0; for (Object obj : objArr) { Cell cell = row.createCell(cellnum++); setCellValue(obj, cell); } } fileName = new String(fileName.getBytes("GBK"), "ISO-8859-1"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); OutputStream ouputStream = response.getOutputStream(); workbook.write(ouputStream); ouputStream.flush(); ouputStream.close(); } private static void setCellValue(Object obj, Cell cell) { if (obj instanceof String) { cell.setCellValue((String) obj); } else if (obj instanceof Integer) { cell.setCellValue((Integer) obj); } else if (obj instanceof BigDecimal) { cell.setCellValue(obj.toString()); } } /** * @param srcfile 文件名数组 * @param zipfile 压缩后文件 */ public static void zipFiles(File[] srcfile, File zipfile) { byte[] buf = new byte[1024]; try { ZipOutputStream out = new ZipOutputStream(new FileOutputStream( zipfile)); for (int i = 0; i < srcfile.length; i++) { FileInputStream in = new FileInputStream(srcfile[i]); out.putNextEntry(new ZipEntry(srcfile[i].getName())); int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } out.closeEntry(); in.close(); } out.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 文件删除 * * @param fileNames * @param zipPath */ public static void deleteFile(List<String> fileNames, String zipPath) { String sPath = null; File file = null; boolean flag = false; try { // 判断目录或文件是否存在 for (int i = 0; i < fileNames.size(); i++) { sPath = fileNames.get(i); file = new File(sPath); if (file.exists()) { file.delete(); } } file = new File(zipPath); if (file.exists()) { file.delete(); } } catch (Exception e) { e.printStackTrace(); } } /** * 导出zip方法(需要有excel文件夹) * @param fileNames * @param path * @param zip * @param out * @param fileName * @throws IOException */ public static void exportZip(List<String> fileNames, String path, File zip, OutputStream out, String fileName) throws IOException { File[] srcfile = new File[fileNames.size()]; for (int i = 0, n = fileNames.size(); i < n; i++) { srcfile[i] = new File(fileNames.get(i)); } ExportExcle.zipFiles(srcfile, zip); FileInputStream inStream = new FileInputStream(zip); try { byte[] buf = new byte[4096]; int readLength; while ((readLength = inStream.read(buf)) != -1) { out.write(buf, 0, readLength); } inStream.close(); ExportExcle.deleteFile(fileNames, path + "excel/" + fileName + ".zip"); } catch (IOException e) { e.printStackTrace(); } finally { inStream.close(); } } }

调用导出 excle 方法 进行参考

/** * excle 会员导出方式 kzyuan * * @param memberFrom * @param startDate * @param endDate * @param response * @param request */ @RequestMapping(value = "exportMemberExcle", method = RequestMethod.GET) public void exportMemberExcle(String memberFrom,String startDate,String endDate,HttpServletResponse response, HttpServletRequest request) { String fileName = "导出会员.xlsx"; String sheetName = 导出会员分页; /** * 获取导出数据 */ List<TheMemberListingBean> list = webmemberService.exportMember(memberFrom,startDate,endDate); /** * 第一列的列宽和列里的内容 */ int[] columnWidths = new int[]{20, 30, 20}; Map<String, Object[]> data = new TreeMap<String, Object[]>(); data.put("1", new Object[]{ "会员编号或邀请码", "手机号", "会员级别" }); /** * 开始输出内容 */ int dataIdx = 2; for (TheMemberListingBean datas : list()) { Object[] newRow = new Object[] { String.valueOf(datas.getInviteCode()), String.valueOf(datas.getUserPhone()), String.valueOf(datas.getMemberType()) }; data.put("" + dataIdx++, newRow); } try { ExportExcle.generateExcel(response, data, fileName, sheetName, columnWidths); } catch (IOException e) { log.error("IOException", e); } }

之前导出需要几万条用户,大约在 1-10 分钟以上,调用这个方法,大约在 20 秒左右,不会超出一分钟,当然速度还跟网络和运行配置有关。
能导出了,终于可以愉快的写代码去了。

当然如果你有更好的替代方案,欢迎在评论里留言。

关于我们

程序员太辛苦了

请善待你们身边的每一位程序员~

欢迎在评论写下你的程序员自黑体呦,嗯,相信你可以滴~~~~~~

以上内容,均来自互联网~

欢迎扫描二维码加入我们的小组织
黑壳网交流群 Qq:200408242


11a84075a304ac57f6d37323512fd24cde9836350b9d80148b282eeaa188b196c2358d4ffd7006cbpng

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 165 关注
  • 黑壳网
    68 引用 • 44 回帖 • 2 关注
  • 壳叔
    48 引用 • 31 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...