区块链技术博客
www.b2bchain.cn

通过sax和流式写实现读写excel的内存占用优化

这篇文章主要介绍了通过sax和流式写实现读写excel的内存占用优化的讲解,通过具体代码实例进行17670 讲解,并且分析了通过sax和流式写实现读写excel的内存占用优化的详细步骤与相关技巧,需要的朋友可以参考下https://www.b2bchain.cn/?p=17670

本文实例讲述了2、树莓派设置连接WiFi,开启VNC等等的讲解。分享给大家供大家参考文章查询地址https://www.b2bchain.cn/7039.html。具体如下:

记一次excel处理优化

    • 刚入职第一个需求
    • 思路
    • 细节

刚入职第一个需求

这是入职做的第一个需求,系统中有一个拼接excel的功能(第一个excel和第二个excel横向拼接成一个新的excel,再继续和下一个excel拼接成一个更大的excel)。通常处理excel会使用jxl或者poi,poi支持excel2007,jxl只支持到excel2003(即以.xls结尾的excel),所以我们选择使用poi来处理excel,但是poi有个很大的缺陷,常用的用户模式内存占用过高,一个3M的excel经过poi读取之后会占用600m甚至更大的内存,所以就有了这个需求:优化内存占用。

思路

poi官网上提供了三种读写excel的方式
通过sax和流式写实现读写excel的内存占用优化
eventmodel即sax模式,cpu和内存占用低,但是只能读,不能写。usermodel即将excel解析成dom的方式,这种方式是最常用的,但是cpu和内存占用很高。sxssf即流式写的方式,cpu和内存占用低,但是只能写,不能读。
看过这三种方式之后,很容易想到使用sax模式来读,使用流式写来写。

细节

首先是实现sax模式,sax模式除了依赖poi相关的包之外,还依赖xmlReader相关的包(xlsx格式的excel是通过xml来存储数据的,sax模式的本质是解析xml),导入包后,需要继承DefaultHandler,重写startElement,endElement,characters三个回调方法。startElement方法用于解析到xml的开标签的时候执行,endElement方法用于解析到闭标签的时候执行,characters用于获取标签中间的数据,也就是单元格中的数据。

    <!-- https://mvnrepository.com/artifact/xerces/xercesImpl -->     <dependency>         <groupId>xerces</groupId>         <artifactId>xercesImpl</artifactId>         <version>2.12.0</version>     </dependency>  
        private static class ExcelDataHandler extends DefaultHandler {         private SharedStringsTable sst;         private List<List<Object>> data;         private List<Object> currentRow;         private Object lastContent;         private int lastRow;         private int lastCol;         private boolean sstRef; // 是否是sst的索引         private boolean numValue; // 是否是数字          public ExcelDataHandler(SharedStringsTable sst) {             this.sst = sst;             this.data = new ArrayList<>();             lastCol = lastRow = -1;         }          public List<List<Object>> getData() {             return data;         }          @Override         public void startElement(String uri, String localName, String qName, Attributes attributes) {             lastContent = "";             if ("row".equals(qName)) { // 行开始                 int row = Integer.parseInt(attributes.getValue("r"));                 if (lastRow >= 0) { // 填充空行                     int gap = row - lastRow;                     while (gap > 1) {                         data.add(new ArrayList<>());                         gap--;                     }                 }                 lastRow = row;                 currentRow = new ArrayList<>();             } else if ("c".equals(qName)) { // 列开始                 int col = getColNum(attributes.getValue("r"));                 int gap = lastCol >= 0 ? col - lastCol : col; // 第一个单元格可能不是在第一列                 while (gap > 1) { //填充空列                     currentRow.add("");                     gap--;                 }                 lastCol = col;                                  String type = attributes.getValue("t");                 if (type != null) {                     sstRef = "s".equals(type);                     numValue = "n".equals(type);                 }             }         }          @Override         public void endElement(String uri, String localName, String qName) throws SAXException {             if ("row".equals(qName)) { // 行结束                 data.add(currentRow);                 lastRow = -1;             } else if ("v".equals(qName) || "t".equals(qName)) {                 if (sstRef) { // SST索引值                     int index = Integer.parseInt((String) lastContent);                     XSSFRichTextString rtss = (XSSFRichTextString) sst.getItemAt(index);                     lastContent = rtss.toString();                     sstRef = false;                 }                 currentRow.add(lastContent);             }         }          @Override         public void characters(char[] ch, int start, int length) throws SAXException {             lastContent = new String(ch, start, length);             if (numValue) {                 lastContent = Double.valueOf((String) lastContent);                 numValue = false;             }         }      }      /**      * 列号转数字,如:AB7-->28 第28列      *      * @param title      *            列号      * @return int      */     public static int getColNum(String title) {         int sum = 0;         for (int i = 0; i < title.length(); ++i) {             int tmp = title.charAt(i) - 'A';             if (tmp < 0){                 break;             }             sum = 26 * sum + tmp + 1;         }         return sum;     } 

接下来是读取完excel之后的业务逻辑相关的处理。
最后是处理完之后将处理完之后的数据通过流式写写出到excel。

    public static void write(List<List<Object>> data, String filePath) {         SXSSFWorkbook wb = new SXSSFWorkbook(100);         wb.setCompressTempFiles(true);         Sheet sh = wb.createSheet();         int rows = data.size();         for (int rowNum = 0; rowNum < rows; rowNum++) {             Row row = sh.createRow(rowNum);             List<Object> list = data.get(rowNum);             int cols = list.size();             for (int cellNum = 0; cellNum < cols; cellNum++) {                 Cell cell = row.createCell(cellNum);                 Object value = list.get(cellNum);                 if (value == null){                     continue;                 }                 if (value instanceof Number) {                     cell.setCellValue(((Number) value).doubleValue());                 } else if (value instanceof String) {                     cell.setCellValue((String) value);                 } else {                     cell.setCellValue(value.toString());                 }             }         }          try (FileOutputStream out = new FileOutputStream(filePath)) {             wb.write(out);             wb.dispose();         } catch (IOException e) {             LOGGER.error(e.getMessage(), e);         } finally {             IOUtils.closeQuietly(wb);         }     } 

本文转自互联网,侵权联系删除通过sax和流式写实现读写excel的内存占用优化

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 通过sax和流式写实现读写excel的内存占用优化
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们