POI处理excel大数据

2007格式:

excel2007文件格式与之前版本不同,之前版本采用的是微软自己的存储格式。07版内容的存储采用XML格式,所以,理所当然的,对大数据量的xlsx文件的读取采用的也是XML的处理方式SAX。

    同之前的版本一样,大数据量文件的读取采用的是事件模型eventusermodel。usermodel模式需要将文件一次性全部读到内存中,07版的既然采用的存储模式是xml,解析用的DOM方式也是如此,这种模式操作简单,容易上手,但是对于大量数据占用的内存也是相当可观,在Eclipse中经常出现内存溢出。

    下面就是采用eventusermodel对07excel文件读取。

    同上篇,我将当前行的单元格数据存储到List中,抽象出 optRows 方法,该方法会在每行末尾时调用,方法参数为当前行索引curRow(int型)及存有行内单元格数据的List。继承类只需实现该行级方法即可。

  1. package com.gaosheng.util.xls;  
  2.   
  3. import java.io.InputStream;  
  4. import java.sql.SQLException;  
  5. import java.util.ArrayList;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8.   
  9. import org.apache.poi.xssf.eventusermodel.XSSFReader;  
  10. import org.apache.poi.xssf.model.SharedStringsTable;  
  11. import org.apache.poi.xssf.usermodel.XSSFRichTextString;  
  12. import org.apache.poi.openxml4j.opc.OPCPackage;  
  13. import org.xml.sax.Attributes;  
  14. import org.xml.sax.InputSource;  
  15. import org.xml.sax.SAXException;  
  16. import org.xml.sax.XMLReader;  
  17. import org.xml.sax.helpers.DefaultHandler;  
  18. import org.xml.sax.helpers.XMLReaderFactory;  
  19.   
  20. /** 
  21.  * XSSF and SAX (Event API) 
  22.  */  
  23. public abstract class XxlsAbstract extends DefaultHandler {  
  24.     private SharedStringsTable sst;  
  25.     private String lastContents;  
  26.     private boolean nextIsString;  
  27.   
  28.     private int sheetIndex = -1;  
  29.     private List<String> rowlist = new ArrayList<String>();  
  30.     private int curRow = 0;     //当前行  
  31.     private int curCol = 0;     //当前列索引  
  32.     private int preCol = 0;     //上一列列索引  
  33.     private int titleRow = 0;   //标题行,一般情况下为0  
  34.     private int rowsize = 0;    //列数  
  35.       
  36.     //excel记录行操作方法,以行索引和行元素列表为参数,对一行元素进行操作,元素为String类型  
  37. //  public abstract void optRows(int curRow, List<String> rowlist) throws SQLException ;  
  38.       
  39.     //excel记录行操作方法,以sheet索引,行索引和行元素列表为参数,对sheet的一行元素进行操作,元素为String类型  编写自己的业务
  40.     public abstract void optRows(int sheetIndex,int curRow, List<String> rowlist) throws SQLException;  
  41.       
  42.     //只遍历一个sheet,其中sheetId为要遍历的sheet索引,从1开始,1-3  
  43.     public void processOneSheet(String filename,int sheetId) throws Exception {  
  44.         OPCPackage pkg = OPCPackage.open(filename);  
  45.         XSSFReader r = new XSSFReader(pkg);  
  46.         SharedStringsTable sst = r.getSharedStringsTable();  
  47.           
  48.         XMLReader parser = fetchSheetParser(sst);  
  49.   
  50.         // rId2 found by processing the Workbook  
  51.         // 根据 rId# 或 rSheet# 查找sheet  
  52.         InputStream sheet2 = r.getSheet("rId"+sheetId);  
  53.         sheetIndex++;  
  54.         InputSource sheetSource = new InputSource(sheet2);  
  55.         parser.parse(sheetSource);  
  56.         sheet2.close();  
  57.     }  
  58.   
  59.     /** 
  60.      * 遍历 excel 文件 
  61.      */  
  62.     public void process(String filename) throws Exception {  
  63.         OPCPackage pkg = OPCPackage.open(filename);  
  64.         XSSFReader r = new XSSFReader(pkg);  
  65.         SharedStringsTable sst = r.getSharedStringsTable();  
  66.   
  67.         XMLReader parser = fetchSheetParser(sst);  
  68.   
  69.         Iterator<InputStream> sheets = r.getSheetsData();  
  70.         while (sheets.hasNext()) {  
  71.             curRow = 0;  
  72.             sheetIndex++;  
  73.             InputStream sheet = sheets.next();  
  74.             InputSource sheetSource = new InputSource(sheet);  
  75.             parser.parse(sheetSource);  
  76.             sheet.close();  
  77.         }  
  78.     }  
  79.   
  80.     public XMLReader fetchSheetParser(SharedStringsTable sst)  
  81.             throws SAXException {  
  82.         XMLReader parser = XMLReaderFactory  
  83.                 .createXMLReader("org.apache.xerces.parsers.SAXParser");  
  84.         this.sst = sst;  
  85.         parser.setContentHandler(this);  
  86.         return parser;  
  87.     }  
  88.   
  89.     public void startElement(String uri, String localName, String name,  
  90.             Attributes attributes) throws SAXException {  
  91.         // c => 单元格  
  92.         if (name.equals("c")) {  
  93.             // 如果下一个元素是 SST 的索引,则将nextIsString标记为true  
  94.             String cellType = attributes.getValue("t");  
  95.             String rowStr = attributes.getValue("r");  
  96.             curCol = this.getRowIndex(rowStr);  
  97.             if (cellType != null && cellType.equals("s")) {  
  98.                 nextIsString = true;  
  99.             } else {  
  100.                 nextIsString = false;  
  101.             }  
  102.         }  
  103.         // 置空  
  104.         lastContents = "";  
  105.     }  
  106.   
  107.     public void endElement(String uri, String localName, String name)  
  108.             throws SAXException {  
  109.         // 根据SST的索引值的到单元格的真正要存储的字符串  
  110.         // 这时characters()方法可能会被调用多次  
  111.         if (nextIsString) {  
  112.             try {  
  113.                 int idx = Integer.parseInt(lastContents);  
  114.                 lastContents = new XSSFRichTextString(sst.getEntryAt(idx))  
  115.                         .toString();  
  116.             } catch (Exception e) {  
  117.   
  118.             }  
  119.         }  
  120.   
  121.         // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引  
  122.         // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符  
  123.         if (name.equals("v")) {  
  124.             String value = lastContents.trim();  
  125.             value = value.equals("")?" ":value;  
  126.             int cols = curCol-preCol;  
  127.             if (cols>1){  
  128.                 for (int i = 0;i < cols-1;i++){  
  129.                     rowlist.add(preCol,"");  
  130.                 }  
  131.             }  
  132.             preCol = curCol;  
  133.             rowlist.add(curCol-1, value);  
  134.         }else {  
  135.             //如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法  
  136.             if (name.equals("row")) {  
  137.                 int tmpCols = rowlist.size();  
  138.                 if(curRow>this.titleRow && tmpCols<this.rowsize){  
  139.                     for (int i = 0;i < this.rowsize-tmpCols;i++){  
  140.                         rowlist.add(rowlist.size(), "");  
  141.                     }  
  142.                 }  
  143.                 try {  
  144.                     optRows(sheetIndex,curRow,rowlist);  
  145.                 } catch (SQLException e) {  
  146.                     e.printStackTrace();  
  147.                 }  
  148.                 if(curRow==this.titleRow){  
  149.                     this.rowsize = rowlist.size();  
  150.                 }  
  151.                 rowlist.clear();  
  152.                 curRow++;  
  153.                 curCol = 0;  
  154.                 preCol = 0;  
  155.             }  
  156.         }  
  157.     }  
  158.   
  159.     public void characters(char[] ch, int start, int length)  
  160.             throws SAXException {  
  161.         //得到单元格内容的值  
  162.         lastContents += new String(ch, start, length);  
  163.     }  
  164.       
  165.     //得到列索引,每一列c元素的r属性构成为字母加数字的形式,字母组合为列索引,数字组合为行索引,  
  166.     //如AB45,表示为第(A-A+1)*26+(B-A+1)*26列,45行  
  167.     public int getRowIndex(String rowStr){  
  168.         rowStr = rowStr.replaceAll("[^A-Z]""");  
  169.         byte[] rowAbc = rowStr.getBytes();  
  170.         int len = rowAbc.length;  
  171.         float num = 0;  
  172.         for (int i=0;i<len;i++){  
  173.             num += (rowAbc[i]-'A'+1)*Math.pow(26,len-i-1 );  
  174.         }  
  175.         return (int) num;  
  176.     }  
  177.   
  178.     public int getTitleRow() {  
  179.         return titleRow;  
  180.     }  
  181.   
  182.     public void setTitleRow(int titleRow) {  
  183.         this.titleRow = titleRow;  
  184.     }  
  185. }  



20003格式:

  1. package com.gaosheng.util.xls;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileNotFoundException;  
  4. import java.io.IOException;  
  5. import java.io.PrintStream;  
  6. import java.sql.SQLException;  
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9.   
  10. import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;  
  11. import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;  
  12. import org.apache.poi.hssf.eventusermodel.HSSFListener;  
  13. import org.apache.poi.hssf.eventusermodel.HSSFRequest;  
  14. import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;  
  15. import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;  
  16. import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;  
  17. import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;  
  18. import org.apache.poi.hssf.model.HSSFFormulaParser;  
  19. import org.apache.poi.hssf.record.BOFRecord;  
  20. import org.apache.poi.hssf.record.BlankRecord;  
  21. import org.apache.poi.hssf.record.BoolErrRecord;  
  22. import org.apache.poi.hssf.record.BoundSheetRecord;  
  23. import org.apache.poi.hssf.record.FormulaRecord;  
  24. import org.apache.poi.hssf.record.LabelRecord;  
  25. import org.apache.poi.hssf.record.LabelSSTRecord;  
  26. import org.apache.poi.hssf.record.NoteRecord;  
  27. import org.apache.poi.hssf.record.NumberRecord;  
  28. import org.apache.poi.hssf.record.RKRecord;  
  29. import org.apache.poi.hssf.record.Record;  
  30. import org.apache.poi.hssf.record.SSTRecord;  
  31. import org.apache.poi.hssf.record.StringRecord;  
  32. import org.apache.poi.hssf.usermodel.HSSFWorkbook;  
  33. import org.apache.poi.poifs.filesystem.POIFSFileSystem;  
  34.   
  35. public abstract class HxlsAbstract implements HSSFListener {  
  36.     private int minColumns;  
  37.     private POIFSFileSystem fs;  
  38.     private PrintStream output;  
  39.   
  40.     private int lastRowNumber;  
  41.     private int lastColumnNumber;  
  42.   
  43.     /** Should we output the formula, or the value it has? */  
  44.     private boolean outputFormulaValues = true;  
  45.   
  46.     /** For parsing Formulas */  
  47.     private SheetRecordCollectingListener workbookBuildingListener;  
  48.     private HSSFWorkbook stubWorkbook;  
  49.   
  50.     // Records we pick up as we process  
  51.     private SSTRecord sstRecord;  
  52.     private FormatTrackingHSSFListener formatListener;  
  53.   
  54.     /** So we known which sheet we're on */  
  55.     private int sheetIndex = -1;  
  56.     private BoundSheetRecord[] orderedBSRs;  
  57.     @SuppressWarnings("unchecked")  
  58.     private ArrayList boundSheetRecords = new ArrayList();  
  59.   
  60.     // For handling formulas with string results  
  61.     private int nextRow;  
  62.     private int nextColumn;  
  63.     private boolean outputNextStringRecord;  
  64.   
  65.     private int curRow;  
  66.     private List<String> rowlist;  
  67.     @SuppressWarnings"unused")  
  68.     private String sheetName;  
  69.   
  70.     public HxlsAbstract(POIFSFileSystem fs)  
  71.             throws SQLException {  
  72.         this.fs = fs;  
  73.         this.output = System.out;  
  74.         this.minColumns = -1;  
  75.         this.curRow = 0;  
  76.         this.rowlist = new ArrayList<String>();  
  77.     }  
  78.   
  79.     public HxlsAbstract(String filename) throws IOException,  
  80.             FileNotFoundException, SQLException {  
  81.         this(new POIFSFileSystem(new FileInputStream(filename)));  
  82.     }  
  83.       
  84.     //excel记录行操作方法,以行索引和行元素列表为参数,对一行元素进行操作,元素为String类型  
  85. //  public abstract void optRows(int curRow, List<String> rowlist) throws SQLException ;  
  86.       
  87.     //excel记录行操作方法,以sheet索引,行索引和行元素列表为参数,对sheet的一行元素进行操作,元素为String类型  
  88.     public abstract void optRows(int sheetIndex,int curRow, List<String> rowlist) throws SQLException;  
  89.       
  90.     /** 
  91.      * 遍历 excel 文件 
  92.      */  
  93.     public void process() throws IOException {  
  94.         MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(  
  95.                 this);  
  96.         formatListener = new FormatTrackingHSSFListener(listener);  
  97.   
  98.         HSSFEventFactory factory = new HSSFEventFactory();  
  99.         HSSFRequest request = new HSSFRequest();  
  100.   
  101.         if (outputFormulaValues) {  
  102.             request.addListenerForAllRecords(formatListener);  
  103.         } else {  
  104.             workbookBuildingListener = new SheetRecordCollectingListener(  
  105.                     formatListener);  
  106.             request.addListenerForAllRecords(workbookBuildingListener);  
  107.         }  
  108.   
  109.         factory.processWorkbookEvents(request, fs);  
  110.     }  
  111.       
  112.     /** 
  113.      * HSSFListener 监听方法,处理 Record 
  114.      */  
  115.     @SuppressWarnings("unchecked")  
  116.     public void processRecord(Record record) {  
  117.         int thisRow = -1;  
  118.         int thisColumn = -1;  
  119.         String thisStr = null;  
  120.         String value = null;  
  121.           
  122.         switch (record.getSid()) {  
  123.         case BoundSheetRecord.sid:  
  124.             boundSheetRecords.add(record);  
  125.             break;  
  126.         case BOFRecord.sid:  
  127.             BOFRecord br = (BOFRecord) record;  
  128.             if (br.getType() == BOFRecord.TYPE_WORKSHEET) {  
  129.                 // Create sub workbook if required  
  130.                 if (workbookBuildingListener != null && stubWorkbook == null) {  
  131.                     stubWorkbook = workbookBuildingListener  
  132.                             .getStubHSSFWorkbook();  
  133.                 }  
  134.   
  135.                 // Works by ordering the BSRs by the location of  
  136.                 // their BOFRecords, and then knowing that we  
  137.                 // process BOFRecords in byte offset order  
  138.                 sheetIndex++;  
  139.                 if (orderedBSRs == null) {  
  140.                     orderedBSRs = BoundSheetRecord  
  141.                             .orderByBofPosition(boundSheetRecords);  
  142.                 }  
  143.                 sheetName = orderedBSRs[sheetIndex].getSheetname();  
  144.             }  
  145.             break;  
  146.   
  147.         case SSTRecord.sid:  
  148.             sstRecord = (SSTRecord) record;  
  149.             break;  
  150.   
  151.         case BlankRecord.sid:  
  152.             BlankRecord brec = (BlankRecord) record;  
  153.   
  154.             thisRow = brec.getRow();  
  155.             thisColumn = brec.getColumn();  
  156.             thisStr = "";  
  157.             break;  
  158.         case BoolErrRecord.sid:  
  159.             BoolErrRecord berec = (BoolErrRecord) record;  
  160.   
  161.             thisRow = berec.getRow();  
  162.             thisColumn = berec.getColumn();  
  163.             thisStr = "";  
  164.             break;  
  165.   
  166.         case FormulaRecord.sid:  
  167.             FormulaRecord frec = (FormulaRecord) record;  
  168.   
  169.             thisRow = frec.getRow();  
  170.             thisColumn = frec.getColumn();  
  171.   
  172.             if (outputFormulaValues) {  
  173.                 if (Double.isNaN(frec.getValue())) {  
  174.                     // Formula result is a string  
  175.                     // This is stored in the next record  
  176.                     outputNextStringRecord = true;  
  177.                     nextRow = frec.getRow();  
  178.                     nextColumn = frec.getColumn();  
  179.                 } else {  
  180.                     thisStr = formatListener.formatNumberDateCell(frec);  
  181.                 }  
  182.             } else {  
  183.                 thisStr = '"' + HSSFFormulaParser.toFormulaString(stubWorkbook,  
  184.                         frec.getParsedExpression()) + '"';  
  185.             }  
  186.             break;  
  187.         case StringRecord.sid:  
  188.             if (outputNextStringRecord) {  
  189.                 // String for formula  
  190.                 StringRecord srec = (StringRecord) record;  
  191.                 thisStr = srec.getString();  
  192.                 thisRow = nextRow;  
  193.                 thisColumn = nextColumn;  
  194.                 outputNextStringRecord = false;  
  195.             }  
  196.             break;  
  197.   
  198.         case LabelRecord.sid:  
  199.             LabelRecord lrec = (LabelRecord) record;  
  200.   
  201.             curRow = thisRow = lrec.getRow();  
  202.             thisColumn = lrec.getColumn();  
  203.             value = lrec.getValue().trim();  
  204.             value = value.equals("")?" ":value;  
  205.             this.rowlist.add(thisColumn, value);  
  206.             break;  
  207.         case LabelSSTRecord.sid:  
  208.             LabelSSTRecord lsrec = (LabelSSTRecord) record;  
  209.   
  210.             curRow = thisRow = lsrec.getRow();  
  211.             thisColumn = lsrec.getColumn();  
  212.             if (sstRecord == null) {  
  213.                 rowlist.add(thisColumn, " ");  
  214.             } else {  
  215.                 value =  sstRecord  
  216.                 .getString(lsrec.getSSTIndex()).toString().trim();  
  217.                 value = value.equals("")?" ":value;  
  218.                 rowlist.add(thisColumn,value);  
  219.             }  
  220.             break;  
  221.         case NoteRecord.sid:  
  222.             NoteRecord nrec = (NoteRecord) record;  
  223.   
  224.             thisRow = nrec.getRow();  
  225.             thisColumn = nrec.getColumn();  
  226.             // TODO: Find object to match nrec.getShapeId()  
  227.             thisStr = '"' + "(TODO)" + '"';  
  228.             break;  
  229.         case NumberRecord.sid:  
  230.             NumberRecord numrec = (NumberRecord) record;  
  231.   
  232.             curRow = thisRow = numrec.getRow();  
  233.             thisColumn = numrec.getColumn();  
  234.             value = formatListener.formatNumberDateCell(numrec).trim();  
  235.             value = value.equals("")?" ":value;  
  236.             // Format  
  237.             rowlist.add(thisColumn, value);  
  238.             break;  
  239.         case RKRecord.sid:  
  240.             RKRecord rkrec = (RKRecord) record;  
  241.   
  242.             thisRow = rkrec.getRow();  
  243.             thisColumn = rkrec.getColumn();  
  244.             thisStr = '"' + "(TODO)" + '"';  
  245.             break;  
  246.         default:  
  247.             break;  
  248.         }  
  249.   
  250.         // 遇到新行的操作  
  251.         if (thisRow != -1 && thisRow != lastRowNumber) {  
  252.             lastColumnNumber = -1;  
  253.         }  
  254.   
  255.         // 空值的操作  
  256.         if (record instanceof MissingCellDummyRecord) {  
  257.             MissingCellDummyRecord mc = (MissingCellDummyRecord) record;  
  258.             curRow = thisRow = mc.getRow();  
  259.             thisColumn = mc.getColumn();  
  260.             rowlist.add(thisColumn," ");  
  261.         }  
  262.   
  263.         // 如果遇到能打印的东西,在这里打印  
  264.         if (thisStr != null) {  
  265.             if (thisColumn > 0) {  
  266.                 output.print(',');  
  267.             }  
  268.             output.print(thisStr);  
  269.         }  
  270.   
  271.         // 更新行和列的值  
  272.         if (thisRow > -1)  
  273.             lastRowNumber = thisRow;  
  274.         if (thisColumn > -1)  
  275.             lastColumnNumber = thisColumn;  
  276.   
  277.         // 行结束时的操作  
  278.         if (record instanceof LastCellOfRowDummyRecord) {  
  279.             if (minColumns > 0) {  
  280.                 // 列值重新置空  
  281.                 if (lastColumnNumber == -1) {  
  282.                     lastColumnNumber = 0;  
  283.                 }  
  284.             }  
  285.             // 行结束时, 调用 optRows() 方法  
  286.             lastColumnNumber = -1;  
  287.             try {  
  288.                 optRows(sheetIndex,curRow, rowlist);  
  289.             } catch (SQLException e) {  
  290.                 e.printStackTrace();  
  291.             }  
  292.             rowlist.clear();  
  293.         }  
  294.     }  
  295. }  


###########大数据导出导出时采用SXSSFWorkbook处理大数据###################
/**创建空模板 利用SXSSF技术,降低内存使用率**/

Workbook wb =  new SXSSFWorkbook(1000);
Sheet sheet = wb.createSheet();

spring在上传excel文件时,有时候会有缓存,即上传的excel解析后会有重复,所有contrllor层时,一定要使用注解@scoper='prototype"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值