POI大数据量导出

前段时间,做POI导出的时候,应为数据量大(十万条数据),所以总是出现OOM错误,在网上找到了解决办法,大数据量导出Excel的方案:http://devbbs.doit.com.cn/redirect.php?tid=46&goto=lastpost
解决大批量数据导出Excel产生内存溢出的方案:http://cnaning.iteye.com/blog/347158
解决大批量数据导出Excel产生内存溢出的方案(二):http://cnaning.iteye.com/blog/347160

  1. import java.io.ByteArrayInputStream;   
  2. import java.io.File;   
  3. import java.io.FileOutputStream;   
  4. import java.io.IOException;   
  5. import java.io.InputStream;   
  6. import java.io.OutputStream;   
  7. import java.util.ArrayList;   
  8. import java.util.Collections;   
  9. import java.util.HashMap;   
  10. import java.util.Iterator;   
  11. import java.util.List;   
  12. import java.util.Map;   
  13.   
  14. import org.apache.log4j.Logger;   
  15. import org.apache.poi.hssf.model.Sheet;   
  16. import org.apache.poi.hssf.model.Workbook;   
  17. import org.apache.poi.hssf.record.CellValueRecordInterface;   
  18. import org.apache.poi.hssf.record.LabelRecord;   
  19. import org.apache.poi.hssf.record.LabelSSTRecord;   
  20. import org.apache.poi.hssf.record.Record;   
  21. import org.apache.poi.hssf.record.RowRecord;   
  22. import org.apache.poi.hssf.record.SSTRecord;   
  23. import org.apache.poi.hssf.record.UnicodeString;   
  24. import org.apache.poi.poifs.filesystem.POIFSFileSystem;   
  25. import org.apache.struts.taglib.html.RewriteTag;   
  26.   
  27. @SuppressWarnings("unchecked")   
  28. public class XlsMergeUtil {   
  29.     private static Logger logger = Logger.getLogger(XlsMergeUtil.class);   
  30.   
  31.     /**  
  32.      * 将多个Xls文件合并为一个,适用于只有一个sheet,并且格式相同的文档  
  33.      *   
  34.      * @param inputs  
  35.      *            输入的Xls文件,第一个XLS文件必须给出足够sheet空间 例如,总共200000行数据,第一个文件至少3个空白sheet  
  36.      * @param out  
  37.      *            输出文件  
  38.      */  
  39.     public static void merge(InputStream[] inputs, OutputStream out) {   
  40.         if (inputs == null || inputs.length <= 1) {   
  41.             throw new IllegalArgumentException("没有传入输入流数组,或只有一个输入流.");   
  42.         }   
  43.   
  44.         List<Record> rootRecords = getRecords(inputs[0]);   
  45.         Workbook workbook = Workbook.createWorkbook(rootRecords);   
  46.         List<Sheet> sheets = getSheets(workbook, rootRecords);   
  47.         if (sheets == null || sheets.size() == 0) {   
  48.             throw new IllegalArgumentException("第一篇文档的格式错误,必须有至少一个sheet");   
  49.         }   
  50.         // 以第一篇文档的第一个sheet为根,以后的数据都追加在这个sheet后面   
  51.         Sheet rootSheet = sheets.get(0);   
  52.         int rootRows = getRowsOfSheet(rootSheet); // 记录第一篇文档的行数,以后的行数在此基础上增加   
  53.         rootSheet.setLoc(rootSheet.getDimsLoc());   
  54.         Map<Integer, Integer> map = new HashMap(10000);   
  55.         int sheetIndex = 0;   
  56.   
  57.         for (int i = 1; i < inputs.length; i++) { // 从第二篇开始遍历   
  58.             List<Record> records = getRecords(inputs[i]);   
  59.             // 达到最大行数限制,换一个sheet   
  60.             if (getRows(records) + rootRows >= RowRecord.MAX_ROW_NUMBER) {   
  61.                 if ((++sheetIndex) > (sheets.size() - 1)) {   
  62.                     logger.warn("第一个文档给出的sheets小于需要的数量,部分数据未能合并.");   
  63.                     break;   
  64.                 }   
  65.                 rootSheet = sheets.get(sheetIndex);   
  66.                 rootRows = getRowsOfSheet(rootSheet);   
  67.                 rootSheet.setLoc(rootSheet.getDimsLoc());   
  68.                 logger.debug("切换Sheet" + sheetIndex + "");   
  69.             }   
  70.             int rowsOfCurXls = 0;   
  71.             // 遍历当前文档的每一个record   
  72.             for (Iterator itr = records.iterator(); itr.hasNext();) {   
  73.                 Record record = (Record) itr.next();   
  74.                 if (record.getSid() == RowRecord.sid) { // 如果是RowRecord   
  75.                     RowRecord rowRecord = (RowRecord) record;   
  76.                     // 调整行号   
  77.                     rowRecord.setRowNumber(rootRows + rowRecord.getRowNumber());   
  78.                     rootSheet.addRow(rowRecord); // 追加Row   
  79.                     rowsOfCurXls++; // 记录当前文档的行数   
  80.                 }   
  81.                 // SST记录,SST保存xls文件中唯一的String,各个String都是对应着SST记录的索引   
  82.                 else if (record.getSid() == SSTRecord.sid) {   
  83.                     SSTRecord sstRecord = (SSTRecord) record;   
  84.                     for (int j = 0; j < sstRecord.getNumUniqueStrings(); j++) {   
  85.                         int index = workbook.addSSTString(sstRecord   
  86.                                 .getString(j));   
  87.                         // 记录原来的索引和现在的索引的对应关系   
  88.                         map.put(Integer.valueOf(j), Integer.valueOf(index));   
  89.                     }   
  90.                 } else if (record.getSid() == LabelSSTRecord.sid) {   
  91.                     LabelSSTRecord label = (LabelSSTRecord) record;   
  92.                     // 调整SST索引的对应关系   
  93.                     label.setSSTIndex(map.get(Integer.valueOf(label   
  94.                             .getSSTIndex())));   
  95.                 }   
  96.                 // 追加ValueCell   
  97.                 if (record instanceof CellValueRecordInterface) {   
  98.                     CellValueRecordInterface cell = (CellValueRecordInterface) record;   
  99.                     int cellRow = cell.getRow() + rootRows;   
  100.                     cell.setRow(cellRow);   
  101.                     rootSheet.addValueRecord(cellRow, cell);   
  102.                 }   
  103.             }   
  104.             rootRows += rowsOfCurXls;   
  105.         }   
  106.   
  107.         byte[] data = getBytes(workbook, sheets.toArray(new Sheet[0]));   
  108.         write(out, data);   
  109.     }   
  110.   
  111.     static void write(OutputStream out, byte[] data) {   
  112.         POIFSFileSystem fs = new POIFSFileSystem();   
  113.         // Write out the Workbook stream   
  114.         try {   
  115.             fs.createDocument(new ByteArrayInputStream(data), "Workbook");   
  116.             fs.writeFilesystem(out);   
  117.             out.flush();   
  118.         } catch (IOException e) {   
  119.             e.printStackTrace();   
  120.         } finally {   
  121.             try {   
  122.                 out.close();   
  123.             } catch (IOException e) {   
  124.                 e.printStackTrace();   
  125.             }   
  126.         }   
  127.     }   
  128.   
  129.     static List<Sheet> getSheets(Workbook workbook, List records) {   
  130.         int recOffset = workbook.getNumRecords();   
  131.         int sheetNum = 0;   
  132.   
  133.         // convert all LabelRecord records to LabelSSTRecord   
  134.         convertLabelRecords(records, recOffset, workbook);   
  135.         List<Sheet> sheets = new ArrayList();   
  136.         while (recOffset < records.size()) {   
  137.             Sheet sh = Sheet.createSheet(records, sheetNum++, recOffset);   
  138.   
  139.             recOffset = sh.getEofLoc() + 1;   
  140.             if (recOffset == 1) {   
  141.                 break;   
  142.             }   
  143.             sheets.add(sh);   
  144.         }   
  145.         return sheets;   
  146.     }   
  147.   
  148.     static int getRows(List<Record> records) {   
  149.         int row = 0;   
  150.         for (Iterator itr = records.iterator(); itr.hasNext();) {   
  151.             Record record = (Record) itr.next();   
  152.             if (record.getSid() == RowRecord.sid) {   
  153.                 row++;   
  154.             }   
  155.         }   
  156.         return row;   
  157.     }   
  158.   
  159.     static int getRowsOfSheet(Sheet sheet) {   
  160.         int rows = 0;   
  161.         sheet.setLoc(0);   
  162.         while (sheet.getNextRow() != null) {   
  163.             rows++;   
  164.         }   
  165.         return rows;   
  166.     }   
  167.   
  168.     @SuppressWarnings("deprecation")   
  169.     static List<Record> getRecords(InputStream input) {   
  170.         try {   
  171.             POIFSFileSystem poifs = new POIFSFileSystem(input);   
  172.             InputStream stream = poifs.getRoot().createDocumentInputStream(   
  173.                     "Workbook");   
  174.             return org.apache.poi.hssf.record.RecordFactory.createRecords(stream);   
  175.         } catch (IOException e) {   
  176.             logger.error("IO异常:" + e.getMessage() + "");   
  177.             e.printStackTrace();   
  178.         }   
  179.         return Collections.EMPTY_LIST;   
  180.     }   
  181.   
  182.     static void convertLabelRecords(List records, int offset, Workbook workbook) {   
  183.   
  184.         for (int k = offset; k < records.size(); k++) {   
  185.             Record rec = (Record) records.get(k);   
  186.   
  187.             if (rec.getSid() == LabelRecord.sid) {   
  188.                 LabelRecord oldrec = (LabelRecord) rec;   
  189.   
  190.                 records.remove(k);   
  191.                 LabelSSTRecord newrec = new LabelSSTRecord();   
  192.                 int stringid = workbook.addSSTString(new UnicodeString(oldrec   
  193.                         .getValue()));   
  194.   
  195.                 newrec.setRow(oldrec.getRow());   
  196.                 newrec.setColumn(oldrec.getColumn());   
  197.                 newrec.setXFIndex(oldrec.getXFIndex());   
  198.                 newrec.setSSTIndex(stringid);   
  199.                 records.add(k, newrec);   
  200.             }   
  201.         }   
  202.     }   
  203.   
  204.     public static byte[] getBytes(Workbook workbook, Sheet[] sheets) {   
  205.         // HSSFSheet[] sheets = getSheets();   
  206.         int nSheets = sheets.length;   
  207.   
  208.         // before getting the workbook size we must tell the sheets that   
  209.         // serialization is about to occur.   
  210.         for (int i = 0; i < nSheets; i++) {   
  211.             sheets[i].preSerialize();   
  212.         }   
  213.   
  214.         int totalsize = workbook.getSize();   
  215.         // pre-calculate all the sheet sizes and set BOF indexes   
  216.         int[] estimatedSheetSizes = new int[nSheets];   
  217.         for (int k = 0; k < nSheets; k++) {   
  218.             workbook.setSheetBof(k, totalsize);   
  219.             int sheetSize = sheets[k].getSize();   
  220.             estimatedSheetSizes[k] = sheetSize;   
  221.             totalsize += sheetSize;   
  222.         }   
  223.         logger.debug("分配内存" + totalsize + "bytes");   
  224.         byte[] retval = new byte[totalsize];   
  225.         int pos = workbook.serialize(0, retval);   
  226.         for (int k = 0; k < nSheets; k++) {   
  227.             int serializedSize = sheets[k].serialize(pos, retval);   
  228.             if (serializedSize != estimatedSheetSizes[k]) {   
  229.                 // Wrong offset values have been passed in the call to   
  230.                 // setSheetBof() above.   
  231.                 // For books with more than one sheet, this discrepancy would   
  232.                 // cause excel   
  233.                 // to report errors and loose data while reading the workbook   
  234.                 throw new IllegalStateException(   
  235.                         "Actual serialized sheet size (" + serializedSize   
  236.                                 + ") differs from pre-calculated size ("  
  237.                                 + estimatedSheetSizes[k] + ") for sheet (" + k   
  238.                                 + ")");   
  239.                 // write mis-aligned offsets either   
  240.             }   
  241.             pos += serializedSize;   
  242.         }   
  243.         return retval;   
  244.     }   
  245.   
  246. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值