POI读写海量Excel

时间:2022-09-08 20:25:18
目前处理Excel的开源javaAPI主要有两种,一是Jxl(Java Excel API),Jxl只支持Excel2003以下的版本。另外一种是Apache的Jakarta POI,相比于Jxl,POI对微软办公文档的支持更加强大,但是它使用复杂,上手慢。POI可支持更高的Excel版本2007。对Excel的读取,POI有两种模式,一是用户模式,这种方式同Jxl的使用很类似,使用简单,都是将文件一次性读到内存,文件小的时候,没有什么问题,当文件大的时候,就会出现OutOfMemory的内存溢出问题。第二种是事件驱动模式,拿Excel2007来说,其内容采用XML的格式来存储,所以处理excel就是解析XML,而目前使用事件驱动模式解析XML的API是SAX(Simple API for XML),这种模型在读取XML文档时,并没有将整个文档读入内存,而是按顺序将整个文档解析完,在解析过程中,会主动产生事件交给程序中相应的处理函数来处理当前内容。因此这种方式对系统资源要求不高,可以处理海量数据。笔者曾经做过测试,这种方法处理一千万条,每条五列的数据花费大约11分钟。可见处理海量数据的文件事件驱动是一个很好的方式。而本文中用到的AbstractExcel2003Reader、AbstractExcel2007Reader对Excel的读取都是采用这种POI的事件驱动模式。至于Excel的写操作,对较高版本的Excel2007,POI提供了很好的支持,主要流程是第一步构建工作薄和电子表格对象,第二步在一个流中构建文本文件,第三步使用流中产生的数据替换模板中的电子表格。这种方式也可以处理海量数据文件。AbstractExcel2007Writer就是使用这种方式进行写操作。对于写入较低版本的Excel2003,POI使用了用户模式来处理,就是将整个文档加载进内存,如果数据量大的话就会出现内存溢出的问题,Excel2003Writer就是使用这种方式。据笔者的测试,如果数据量大于3万条,每条8列的话,就会报OutOfMemory的错误。Excel2003中每个电子表格的记录数必须在65536以下,否则就会发生异常。目前还没有好的解决方案,建议对于海量数据写入操作,尽量使用Excel2007。 [java] view plaincopy
  1. /** 
  2.  * 抽象Excel2003读取器,通过实现HSSFListener监听器,采用事件驱动模式解析excel2003 
  3.  * 中的内容,遇到特定事件才会触发,大大减少了内存的使用。 
  4.  * 
  5.  */  
  6. public  class Excel2003Reader implements HSSFListener{  
  7.     private int minColumns = -1;  
  8.     private POIFSFileSystem fs;  
  9.     private int lastRowNumber;  
  10.     private int lastColumnNumber;  
  11.   
  12.     /** Should we output the formula, or the value it has? */  
  13.     private boolean outputFormulaValues = true;  
  14.   
  15.     /** For parsing Formulas */  
  16.     private SheetRecordCollectingListener workbookBuildingListener;  
  17.     //excel2003工作薄  
  18.     private HSSFWorkbook stubWorkbook;  
  19.   
  20.     // Records we pick up as we process  
  21.     private SSTRecord sstRecord;  
  22.     private FormatTrackingHSSFListener formatListener;  
  23.   
  24.     //表索引  
  25.     private int sheetIndex = -1;  
  26.     private BoundSheetRecord[] orderedBSRs;  
  27.     @SuppressWarnings("unchecked")  
  28.     private ArrayList boundSheetRecords = new ArrayList();  
  29.   
  30.     // For handling formulas with string results  
  31.     private int nextRow;  
  32.     private int nextColumn;  
  33.     private boolean outputNextStringRecord;  
  34.     //当前行  
  35.     private int curRow = 0;  
  36.     //存储行记录的容器  
  37.     private List<String> rowlist = new ArrayList<String>();;  
  38.     @SuppressWarnings"unused")  
  39.     private String sheetName;  
  40.       
  41.     private IRowReader rowReader;  
  42.   
  43.       
  44.     public void setRowReader(IRowReader rowReader){  
  45.         this.rowReader = rowReader;  
  46.     }  
  47.       
  48.     /** 
  49.      * 遍历excel下所有的sheet 
  50.      * @throws IOException 
  51.      */  
  52.     public void process(String fileName) throws IOException {  
  53.         this.fs = new POIFSFileSystem(new FileInputStream(fileName));  
  54.         MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(  
  55.                 this);  
  56.         formatListener = new FormatTrackingHSSFListener(listener);  
  57.         HSSFEventFactory factory = new HSSFEventFactory();  
  58.         HSSFRequest request = new HSSFRequest();  
  59.         if (outputFormulaValues) {  
  60.             request.addListenerForAllRecords(formatListener);  
  61.         } else {  
  62.             workbookBuildingListener = new SheetRecordCollectingListener(  
  63.                     formatListener);  
  64.             request.addListenerForAllRecords(workbookBuildingListener);  
  65.         }  
  66.         factory.processWorkbookEvents(request, fs);  
  67.     }  
  68.       
  69.     /** 
  70.      * HSSFListener 监听方法,处理 Record 
  71.      */  
  72.     @SuppressWarnings("unchecked")  
  73.     public void processRecord(Record record) {  
  74.         int thisRow = -1;  
  75.         int thisColumn = -1;  
  76.         String thisStr = null;  
  77.         String value = null;  
  78.         switch (record.getSid()) {  
  79.             case BoundSheetRecord.sid:  
  80.                 boundSheetRecords.add(record);  
  81.                 break;  
  82.             case BOFRecord.sid:  
  83.                 BOFRecord br = (BOFRecord) record;  
  84.                 if (br.getType() == BOFRecord.TYPE_WORKSHEET) {  
  85.                     // 如果有需要,则建立子工作薄  
  86.                     if (workbookBuildingListener != null && stubWorkbook == null) {  
  87.                         stubWorkbook = workbookBuildingListener  
  88.                                 .getStubHSSFWorkbook();  
  89.                     }  
  90.                       
  91.                     sheetIndex++;  
  92.                     if (orderedBSRs == null) {  
  93.                         orderedBSRs = BoundSheetRecord  
  94.                                 .orderByBofPosition(boundSheetRecords);  
  95.                     }  
  96.                     sheetName = orderedBSRs[sheetIndex].getSheetname();  
  97.                 }  
  98.                 break;  
  99.       
  100.             case SSTRecord.sid:  
  101.                 sstRecord = (SSTRecord) record;  
  102.                 break;  
  103.       
  104.             case BlankRecord.sid:  
  105.                 BlankRecord brec = (BlankRecord) record;  
  106.                 thisRow = brec.getRow();  
  107.                 thisColumn = brec.getColumn();  
  108.                 thisStr = "";  
  109.                 rowlist.add(thisColumn, thisStr);  
  110.                 break;  
  111.             case BoolErrRecord.sid: //单元格为布尔类型  
  112.                 BoolErrRecord berec = (BoolErrRecord) record;  
  113.                 thisRow = berec.getRow();  
  114.                 thisColumn = berec.getColumn();  
  115.                 thisStr = berec.getBooleanValue()+"";  
  116.                 rowlist.add(thisColumn, thisStr);  
  117.                 break;  
  118.       
  119.             case FormulaRecord.sid: //单元格为公式类型  
  120.                 FormulaRecord frec = (FormulaRecord) record;  
  121.                 thisRow = frec.getRow();  
  122.                 thisColumn = frec.getColumn();  
  123.                 if (outputFormulaValues) {  
  124.                     if (Double.isNaN(frec.getValue())) {  
  125.                         // Formula result is a string  
  126.                         // This is stored in the next record  
  127.                         outputNextStringRecord = true;  
  128.                         nextRow = frec.getRow();  
  129.                         nextColumn = frec.getColumn();  
  130.                     } else {  
  131.                         thisStr = formatListener.formatNumberDateCell(frec);  
  132.                     }  
  133.                 } else {  
  134.                     thisStr = '"' + HSSFFormulaParser.toFormulaString(stubWorkbook,  
  135.                             frec.getParsedExpression()) + '"';  
  136.                 }  
  137.                 rowlist.add(thisColumn,thisStr);  
  138.                 break;  
  139.             case StringRecord.sid://单元格中公式的字符串  
  140.                 if (outputNextStringRecord) {  
  141.                     // String for formula  
  142.                     StringRecord srec = (StringRecord) record;  
  143.                     thisStr = srec.getString();  
  144.                     thisRow = nextRow;  
  145.                     thisColumn = nextColumn;  
  146.                     outputNextStringRecord = false;  
  147.                 }  
  148.                 break;  
  149.             case LabelRecord.sid:  
  150.                 LabelRecord lrec = (LabelRecord) record;  
  151.                 curRow = thisRow = lrec.getRow();  
  152.                 thisColumn = lrec.getColumn();  
  153.                 value = lrec.getValue().trim();  
  154.                 value = value.equals("")?" ":value;  
  155.                 this.rowlist.add(thisColumn, value);  
  156.                 break;  
  157.             case LabelSSTRecord.sid:  //单元格为字符串类型  
  158.                 LabelSSTRecord lsrec = (LabelSSTRecord) record;  
  159.                 curRow = thisRow = lsrec.getRow();  
  160.                 thisColumn = lsrec.getColumn();  
  161.                 if (sstRecord == null) {  
  162.                     rowlist.add(thisColumn, " ");  
  163.                 } else {  
  164.                     value =  sstRecord  
  165.                     .getString(lsrec.getSSTIndex()).toString().trim();  
  166.                     value = value.equals("")?" ":value;  
  167.                     rowlist.add(thisColumn,value);  
  168.                 }  
  169.                 break;  
  170.             case NumberRecord.sid:  //单元格为数字类型  
  171.                 NumberRecord numrec = (NumberRecord) record;  
  172.                 curRow = thisRow = numrec.getRow();  
  173.                 thisColumn = numrec.getColumn();  
  174.                 value = formatListener.formatNumberDateCell(numrec).trim();  
  175.                 value = value.equals("")?" ":value;  
  176.                 // 向容器加入列值  
  177.                 rowlist.add(thisColumn, value);  
  178.                 break;  
  179.             default:  
  180.                 break;  
  181.         }  
  182.   
  183.         // 遇到新行的操作  
  184.         if (thisRow != -1 && thisRow != lastRowNumber) {  
  185.             lastColumnNumber = -1;  
  186.         }  
  187.   
  188.         // 空值的操作  
  189.         if (record instanceof MissingCellDummyRecord) {  
  190.             MissingCellDummyRecord mc = (MissingCellDummyRecord) record;  
  191.             curRow = thisRow = mc.getRow();  
  192.             thisColumn = mc.getColumn();  
  193.             rowlist.add(thisColumn," ");  
  194.         }  
  195.   
  196.         // 更新行和列的值  
  197.         if (thisRow > -1)  
  198.             lastRowNumber = thisRow;  
  199.         if (thisColumn > -1)  
  200.             lastColumnNumber = thisColumn;  
  201.   
  202.         // 行结束时的操作  
  203.         if (record instanceof LastCellOfRowDummyRecord) {  
  204.             if (minColumns > 0) {  
  205.                 // 列值重新置空  
  206.                 if (lastColumnNumber == -1) {  
  207.                     lastColumnNumber = 0;  
  208.                 }  
  209.             }  
  210.             lastColumnNumber = -1;  
  211.                 // 每行结束时, 调用getRows() 方法  
  212.             rowReader.getRows(sheetIndex,curRow, rowlist);  
  213.               
  214.             // 清空容器  
  215.             rowlist.clear();  
  216.         }  
  217.     }  
  218.       
  219. }  
[java] view plaincopy
  1. /** 
  2.  * 抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析 
  3.  * xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低 
  4.  * 内存的耗费,特别使用于大数据量的文件。 
  5.  * 
  6.  */  
  7. public class Excel2007Reader extends DefaultHandler {  
  8.     //共享字符串表  
  9.     private SharedStringsTable sst;  
  10.     //上一次的内容  
  11.     private String lastContents;  
  12.     private boolean nextIsString;  
  13.   
  14.     private int sheetIndex = -1;  
  15.     private List<String> rowlist = new ArrayList<String>();  
  16.     //当前行  
  17.     private int curRow = 0;  
  18.     //当前列  
  19.     private int curCol = 0;  
  20.     //日期标志  
  21.     private boolean dateFlag;  
  22.     //数字标志  
  23.     private boolean numberFlag;  
  24.       
  25.     private boolean isTElement;  
  26.       
  27.     private IRowReader rowReader;  
  28.       
  29.     public void setRowReader(IRowReader rowReader){  
  30.         this.rowReader = rowReader;  
  31.     }  
  32.       
  33.     /**只遍历一个电子表格,其中sheetId为要遍历的sheet索引,从1开始,1-3 
  34.      * @param filename 
  35.      * @param sheetId 
  36.      * @throws Exception 
  37.      */  
  38.     public void processOneSheet(String filename,int sheetId) throws Exception {  
  39.         OPCPackage pkg = OPCPackage.open(filename);  
  40.         XSSFReader r = new XSSFReader(pkg);  
  41.         SharedStringsTable sst = r.getSharedStringsTable();  
  42.         XMLReader parser = fetchSheetParser(sst);  
  43.           
  44.         // 根据 rId# 或 rSheet# 查找sheet  
  45.         InputStream sheet2 = r.getSheet("rId"+sheetId);  
  46.         sheetIndex++;  
  47.         InputSource sheetSource = new InputSource(sheet2);  
  48.         parser.parse(sheetSource);  
  49.         sheet2.close();  
  50.     }  
  51.   
  52.     /** 
  53.      * 遍历工作簿中所有的电子表格 
  54.      * @param filename 
  55.      * @throws Exception 
  56.      */  
  57.     public void process(String filename) throws Exception {  
  58.         OPCPackage pkg = OPCPackage.open(filename);  
  59.         XSSFReader r = new XSSFReader(pkg);  
  60.         SharedStringsTable sst = r.getSharedStringsTable();  
  61.         XMLReader parser = fetchSheetParser(sst);  
  62.         Iterator<InputStream> sheets = r.getSheetsData();  
  63.         while (sheets.hasNext()) {  
  64.             curRow = 0;  
  65.             sheetIndex++;  
  66.             InputStream sheet = sheets.next();  
  67.             InputSource sheetSource = new InputSource(sheet);  
  68.             parser.parse(sheetSource);  
  69.             sheet.close();  
  70.         }  
  71.     }  
  72.   
  73.     public XMLReader fetchSheetParser(SharedStringsTable sst)  
  74.             throws SAXException {  
  75.         XMLReader parser = XMLReaderFactory  
  76.                 .createXMLReader("org.apache.xerces.parsers.SAXParser");  
  77.         this.sst = sst;  
  78.         parser.setContentHandler(this);  
  79.         return parser;  
  80.     }  
  81.   
  82.     public void startElement(String uri, String localName, String name,  
  83.             Attributes attributes) throws SAXException {  
  84.           
  85.         // c => 单元格  
  86.         if ("c".equals(name)) {  
  87.             // 如果下一个元素是 SST 的索引,则将nextIsString标记为true  
  88.             String cellType = attributes.getValue("t");  
  89.             if ("s".equals(cellType)) {  
  90.                 nextIsString = true;  
  91.             } else {  
  92.                 nextIsString = false;  
  93.             }  
  94.             //日期格式  
  95.             String cellDateType = attributes.getValue("s");  
  96.             if ("1".equals(cellDateType)){  
  97.                 dateFlag = true;  
  98.             } else {  
  99.                 dateFlag = false;  
  100.             }  
  101.             String cellNumberType = attributes.getValue("s");  
  102.             if("2".equals(cellNumberType)){  
  103.                 numberFlag = true;  
  104.             } else {  
  105.                 numberFlag = false;  
  106.             }  
  107.               
  108.         }  
  109.         //当元素为t时  
  110.         if("t".equals(name)){  
  111.             isTElement = true;  
  112.         } else {  
  113.             isTElement = false;  
  114.         }  
  115.           
  116.         // 置空  
  117.         lastContents = "";  
  118.     }  
  119.   
  120.     public void endElement(String uri, String localName, String name)  
  121.             throws SAXException {  
  122.           
  123.         // 根据SST的索引值的到单元格的真正要存储的字符串  
  124.         // 这时characters()方法可能会被调用多次  
  125.         if (nextIsString) {  
  126.             try {  
  127.                 int idx = Integer.parseInt(lastContents);  
  128.                 lastContents = new XSSFRichTextString(sst.getEntryAt(idx))  
  129.                         .toString();  
  130.             } catch (Exception e) {  
  131.   
  132.             }  
  133.         }   
  134.         //t元素也包含字符串  
  135.         if(isTElement){  
  136.             String value = lastContents.trim();  
  137.             rowlist.add(curCol, value);  
  138.             curCol++;  
  139.             isTElement = false;  
  140.             // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引  
  141.             // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符  
  142.         } else if ("v".equals(name)) {  
  143.             String value = lastContents.trim();  
  144.             value = value.equals("")?" ":value;  
  145.             //日期格式处理  
  146.             if(dateFlag){  
  147.                  Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));  
  148.                  SimpleDateFormat dateFormat = new SimpleDateFormat(  
  149.                  "dd/MM/yyyy");  
  150.                  value = dateFormat.format(date);  
  151.             }   
  152.             //数字类型处理  
  153.             if(numberFlag){  
  154.                 BigDecimal bd = new BigDecimal(value);  
  155.                 value = bd.setScale(3,BigDecimal.ROUND_UP).toString();  
  156.             }  
  157.             rowlist.add(curCol, value);  
  158.             curCol++;  
  159.         }else {  
  160.             //如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法  
  161.             if (name.equals("row")) {  
  162.                 rowReader.getRows(sheetIndex,curRow,rowlist);  
  163.                 rowlist.clear();  
  164.                 curRow++;  
  165.                 curCol = 0;  
  166.             }  
  167.         }  
  168.           
  169.     }  
  170.   
  171.     public void characters(char[] ch, int start, int length)  
  172.             throws SAXException {  
  173.         //得到单元格内容的值  
  174.         lastContents += new String(ch, start, length);  
  175.     }  
  176. }  


[java] view plaincopy
  1. public class ExcelReaderUtil {  
  2.       
  3.     //excel2003扩展名  
  4.     public static final String EXCEL03_EXTENSION = ".xls";  
  5.     //excel2007扩展名  
  6.     public static final String EXCEL07_EXTENSION = ".xlsx";  
  7.       
  8.     /** 
  9.      * 读取Excel文件,可能是03也可能是07版本 
  10.      * @param excel03 
  11.      * @param excel07 
  12.      * @param fileName 
  13.      * @throws Exception  
  14.      */  
  15.     public static void readExcel(IRowReader reader,String fileName) throws Exception{  
  16.         // 处理excel2003文件  
  17.         if (fileName.endsWith(EXCEL03_EXTENSION)){  
  18.             Excel2003Reader excel03 = new Excel2003Reader();  
  19.             excel03.setRowReader(reader);  
  20.             excel03.process(fileName);  
  21.         // 处理excel2007文件  
  22.         } else if (fileName.endsWith(EXCEL07_EXTENSION)){  
  23.             Excel2007Reader excel07 = new Excel2007Reader();  
  24.             excel07.setRowReader(reader);  
  25.             excel07.process(fileName);  
  26.         } else {  
  27.             throw new  Exception("文件格式错误,fileName的扩展名只能是xls或xlsx。");  
  28.         }  
  29.     }  
  30. }  


[java] view plaincopy
  1. public interface IRowReader {  
  2.       
  3.     /**业务逻辑实现方法 
  4.      * @param sheetIndex 
  5.      * @param curRow 
  6.      * @param rowlist 
  7.      */  
  8.     public  void getRows(int sheetIndex,int curRow, List<String> rowlist);  
  9. }  


[java] view plaincopy
  1. public class RowReader implements IRowReader{  
  2.   
  3.   
  4.     /* 业务逻辑实现方法 
  5.      * @see com.eprosun.util.excel.IRowReader#getRows(int, int, java.util.List) 
  6.      */  
  7.     public void getRows(int sheetIndex, int curRow, List<String> rowlist) {  
  8.         // TODO Auto-generated method stub  
  9.         System.out.print(curRow+" ");  
  10.         for (int i = 0; i < rowlist.size(); i++) {  
  11.             System.out.print(rowlist.get(i) + " ");  
  12.         }  
  13.         System.out.println();  
  14.     }  
  15.   
  16. }  

[java] view plaincopy
  1. public class Main {  
  2.       
  3.     public static void main(String[] args) throws Exception {  
  4.         IRowReader reader = new RowReader();  
  5.         //ExcelReaderUtil.readExcel(reader, "F://te03.xls");  
  6.         ExcelReaderUtil.readExcel(reader, "F://test07.xlsx");  
  7.     }  
  8. }  


[java] view plaincopy
  1. public class Excel2003Writer {  
  2.   
  3.     /** 
  4.      * @param args 
  5.      */  
  6.     public static void main(String[] args) {  
  7.         try{      
  8.             System.out.println("开始写入excel2003....");  
  9.             writeExcel("tes2003.xls");  
  10.             System.out.println("写完xcel2003");  
  11.         } catch (IOException e) {  
  12.           
  13.         }  
  14.     }  
  15.       
  16.       
  17.     /** 
  18.      * 写入excel并填充内容,一个sheet只能写65536行以下,超出会报异常,写入时建议使用AbstractExcel2007Writer 
  19.      * @param fileName 
  20.      * @throws IOException 
  21.      */  
  22.     public static void writeExcel(String fileName) throws IOException{  
  23.               
  24.             // 创建excel2003对象  
  25.             Workbook wb = new HSSFWorkbook();  
  26.               
  27.             // 设置文件放置路径和文件名  
  28.             FileOutputStream fileOut = new FileOutputStream(fileName);  
  29.             // 创建新的表单  
  30.             Sheet sheet = wb.createSheet("newsheet");  
  31.             // 创建新行  
  32.             for(int i=0;i<20000;i++){  
  33.                 Row row = sheet.createRow(i);  
  34.                 // 创建单元格  
  35.                 Cell cell = row.createCell(0);  
  36.                 // 设置单元格值  
  37.                 cell.setCellValue(1);  
  38.                 row.createCell(1).setCellValue(1+i);  
  39.                 row.createCell(2).setCellValue(true);  
  40.                 row.createCell(3).setCellValue(0.43d);  
  41.                 row.createCell(4).setCellValue('d');  
  42.                 row.createCell(5).setCellValue("");  
  43.                 row.createCell(6).setCellValue("第七列"+i);  
  44.                 row.createCell(7).setCellValue("第八列"+i);  
  45.             }  
  46.             wb.write(fileOut);  
  47.             fileOut.close();  
  48.     }  
  49.   
  50.   
  51. }  


[java] view plaincopy
  1. /** 
  2.  * 抽象excel2007读入器,先构建.xlsx一张模板,改写模板中的sheet.xml,使用这种方法 
  3.  * 写入.xlsx文件,不需要太大的内存 
  4.  * 
  5.  */  
  6. public abstract class AbstractExcel2007Writer {  
  7.       
  8.     private SpreadsheetWriter sw;  
  9.   
  10.     /** 
  11.      * 写入电子表格的主要流程 
  12.      * @param fileName 
  13.      * @throws Exception 
  14.      */  
  15.     public void process(String fileName) throws Exception{  
  16.         // 建立工作簿和电子表格对象  
  17.         XSSFWorkbook wb = new XSSFWorkbook();  
  18.         XSSFSheet sheet = wb.createSheet("sheet1");  
  19.         // 持有电子表格数据的xml文件名 例如 /xl/worksheets/sheet1.xml  
  20.         String sheetRef = sheet.getPackagePart().getPartName().getName();  
  21.   
  22.         // 保存模板  
  23.         FileOutputStream os = new FileOutputStream("template.xlsx");  
  24.         wb.write(os);  
  25.         os.close();  
  26.           
  27.         // 生成xml文件  
  28.         File tmp = File.createTempFile("sheet"".xml");  
  29.         Writer fw = new FileWriter(tmp);  
  30.         sw = new SpreadsheetWriter(fw);  
  31.         generate();  
  32.         fw.close();  
  33.           
  34.         // 使用产生的数据替换模板  
  35.         File templateFile = new File("template.xlsx");  
  36.         FileOutputStream out = new FileOutputStream(fileName);  
  37.         substitute(templateFile, tmp, sheetRef.substring(1), out);  
  38.         out.close();  
  39.         //删除文件之前调用一下垃圾回收器,否则无法删除模板文件  
  40.         System.gc();  
  41.         // 删除临时模板文件  
  42.         if (templateFile.isFile()&&templateFile.exists()){  
  43.             templateFile.delete();  
  44.         }  
  45.     }  
  46.   
  47.     /** 
  48.      * 类使用者应该使用此方法进行写操作 
  49.      * @throws Exception 
  50.      */  
  51.     public abstract void generate() throws Exception;  
  52.   
  53.     public void beginSheet() throws IOException {  
  54.         sw.beginSheet();  
  55.     }  
  56.   
  57.     public void insertRow(int rowNum) throws IOException {  
  58.         sw.insertRow(rowNum);  
  59.     }  
  60.   
  61.     public void createCell(int columnIndex, String value) throws IOException {  
  62.         sw.createCell(columnIndex, value, -1);  
  63.     }  
  64.   
  65.     public void createCell(int columnIndex, double value) throws IOException {  
  66.         sw.createCell(columnIndex, value, -1);  
  67.     }  
  68.   
  69.     public void endRow() throws IOException {  
  70.         sw.endRow();  
  71.     }  
  72.   
  73.     public void endSheet() throws IOException {  
  74.         sw.endSheet();  
  75.     }  
  76.   
  77.     /** 
  78.      * 
  79.      * @param zipfile the template file 
  80.      * @param tmpfile the XML file with the sheet data 
  81.      * @param entry the name of the sheet entry to substitute, e.g. xl/worksheets/sheet1.xml 
  82.      * @param out the stream to write the result to 
  83.      */  
  84.     private static void substitute(File zipfile, File tmpfile, String entry,  
  85.             OutputStream out) throws IOException {  
  86.         ZipFile zip = new ZipFile(zipfile);  
  87.         ZipOutputStream zos = new ZipOutputStream(out);  
  88.   
  89.         @SuppressWarnings("unchecked")  
  90.         Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries();  
  91.         while (en.hasMoreElements()) {  
  92.             ZipEntry ze = en.nextElement();  
  93.             if (!ze.getName().equals(entry)) {  
  94.                 zos.putNextEntry(new ZipEntry(ze.getName()));  
  95.                 InputStream is = zip.getInputStream(ze);  
  96.                 copyStream(is, zos);  
  97.                 is.close();  
  98.             }  
  99.         }  
  100.         zos.putNextEntry(new ZipEntry(entry));  
  101.         InputStream is = new FileInputStream(tmpfile);  
  102.         copyStream(is, zos);  
  103.         is.close();  
  104.         zos.close();  
  105.     }  
  106.   
  107.     private static void copyStream(InputStream in, OutputStream out)  
  108.             throws IOException {  
  109.         byte[] chunk = new byte[1024];  
  110.         int count;  
  111.         while ((count = in.read(chunk)) >= 0) {  
  112.             out.write(chunk, 0, count);  
  113.         }  
  114.     }  
  115.   
  116.     /** 
  117.      * 在写入器中写入电子表格 
  118.      *  
  119.      */  
  120.     public static class SpreadsheetWriter {  
  121.         private final Writer _out;  
  122.         private int _rownum;  
  123.         private static String LINE_SEPARATOR = System.getProperty("line.separator");  
  124.   
  125.         public SpreadsheetWriter(Writer out) {  
  126.             _out = out;  
  127.         }  
  128.   
  129.         public void beginSheet() throws IOException {  
  130.             _out.write("<?xml version=\"1.0\" encoding=\"GB2312\"?>"  
  131.                             + "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");  
  132.             _out.write("<sheetData>"+LINE_SEPARATOR);  
  133.         }  
  134.   
  135.         public void endSheet() throws IOException {  
  136.             _out.write("</sheetData>");  
  137.             _out.write("</worksheet>");  
  138.         }  
  139.   
  140.         /** 
  141.          * 插入新行 
  142.          * 
  143.          * @param rownum 以0开始 
  144.          */  
  145.         public void insertRow(int rownum) throws IOException {  
  146.             _out.write("<row r=\"" + (rownum + 1) + "\">"+LINE_SEPARATOR);  
  147.             this._rownum = rownum;  
  148.         }  
  149.   
  150.         /** 
  151.          * 插入行结束标志 
  152.          */  
  153.         public void endRow() throws IOException {  
  154.             _out.write("</row>"+LINE_SEPARATOR);  
  155.         }  
  156.   
  157.         /** 
  158.          * 插入新列 
  159.          * @param columnIndex 
  160.          * @param value 
  161.          * @param styleIndex 
  162.          * @throws IOException 
  163.          */  
  164.         public void createCell(int columnIndex, String value, int styleIndex)  
  165.                 throws IOException {  
  166.             String ref = new CellReference(_rownum, columnIndex)  
  167.                     .formatAsString();  
  168.             _out.write("<c r=\"" + ref + "\" t=\"inlineStr\"");  
  169.             if (styleIndex != -1)  
  170.                 _out.write(" s=\"" + styleIndex + "\"");  
  171.             _out.write(">");  
  172.             _out.write("<is><t>"+XMLEncoder.encode(value)+"</t></is>");  
  173.             _out.write("</c>");  
  174.         }  
  175.   
  176.         public void createCell(int columnIndex, String value)  
  177.                 throws IOException {  
  178.             createCell(columnIndex, value, -1);  
  179.         }  
  180.   
  181.         public void createCell(int columnIndex, double value, int styleIndex)  
  182.                 throws IOException {  
  183.             String ref = new CellReference(_rownum, columnIndex)  
  184.                     .formatAsString();  
  185.             _out.write("<c r=\"" + ref + "\" t=\"n\"");  
  186.             if (styleIndex != -1)  
  187.                 _out.write(" s=\"" + styleIndex + "\"");  
  188.             _out.write(">");  
  189.             _out.write("<v>" + value + "</v>");  
  190.             _out.write("</c>");  
  191.         }  
  192.   
  193.         public void createCell(int columnIndex, double value)  
  194.                 throws IOException {  
  195.             createCell(columnIndex, value, -1);  
  196.         }  
  197.   
  198.         public void createCell(int columnIndex, Calendar value, int styleIndex)  
  199.                 throws IOException {  
  200.             createCell(columnIndex, DateUtil.getExcelDate(value, false),  
  201.                     styleIndex);  
  202.         }  
  203.     }  
  204. }  

[java] view plaincopy
  1. public class Excel2007WriterImpl extends AbstractExcel2007Writer{  
  2.   
  3.       
  4.     /** 
  5.      * @param args 
  6.      * @throws Exception 
  7.      */  
  8.     public static void main(String[] args) throws Exception {  
  9.         // TODO Auto-generated method stub  
  10.         System.out.println("............................");  
  11.         long start = System.currentTimeMillis();  
  12.         //构建excel2007写入器  
  13.         AbstractExcel2007Writer excel07Writer = new Excel2007WriterImpl();  
  14.         //调用处理方法  
  15.         excel07Writer.process("F://test07.xlsx");  
  16.         long end = System.currentTimeMillis();  
  17.         System.out.println("....................."+(end-start)/1000);  
  18.     }  
  19.   
  20.       
  21.     /*  
  22.      * 可根据需求重写此方法,对于单元格的小数或者日期格式,会出现精度问题或者日期格式转化问题,建议使用字符串插入方法 
  23.      * @see com.excel.ver2.AbstractExcel2007Writer#generate() 
  24.      */  
  25.     @Override  
  26.     public void generate()throws Exception {  
  27.         //电子表格开始  
  28.         beginSheet();  
  29.         for (int rownum = 0; rownum < 100; rownum++) {  
  30.             //插入新行  
  31.             insertRow(rownum);  
  32.             //建立新单元格,索引值从0开始,表示第一列  
  33.             createCell(0"中国<" + rownum + "!");  
  34.             createCell(134343.123456789);  
  35.             createCell(2"23.67%");  
  36.             createCell(3"12:12:23");  
  37.             createCell(4"2010-10-11 12:12:23");  
  38.             createCell(5"true");  
  39.             createCell(6"false");  
  40.             
  41.             //结束行  
  42.             endRow();  
  43.         }  
  44.         //电子表格结束  
  45.         endSheet();  
  46.     }  
  47.   
  48. }  


[java] view plaincopy
  1. public class XMLEncoder {  
  2.   
  3.     private static final String[] xmlCode = new String[256];  
  4.   
  5.     static {  
  6.         // Special characters  
  7.         xmlCode['\''] = "'";  
  8.         xmlCode['\"'] = """// double quote  
  9.         xmlCode['&'] = "&"// ampersand  
  10.         xmlCode['<'] = "<"// lower than  
  11.         xmlCode['>'] = ">"// greater than  
  12.     }  
  13.   
  14.     /** 
  15.      * <p> 
  16.      * Encode the given text into xml. 
  17.      * </p> 
  18.      *  
  19.      * @param string the text to encode 
  20.      * @return the encoded string 
  21.      */  
  22.     public static String encode(String string) {  
  23.         if (string == nullreturn "";  
  24.         int n = string.length();  
  25.         char character;  
  26.         String xmlchar;  
  27.         StringBuffer buffer = new StringBuffer();  
  28.         // loop over all the characters of the String.  
  29.         for (int i = 0; i < n; i++) {  
  30.             character = string.charAt(i);  
  31.             // the xmlcode of these characters are added to a StringBuffer one by one  
  32.             try {  
  33.                 xmlchar = xmlCode[character];  
  34.                 if (xmlchar == null) {  
  35.                     buffer.append(character);  
  36.                 } else {  
  37.                     buffer.append(xmlCode[character]);  
  38.                 }  
  39.             } catch (ArrayIndexOutOfBoundsException aioobe) {  
  40.                 buffer.append(character);  
  41.             }  
  42.         }  
  43.         return buffer.toString();  
  44.     }  
  45.   
  46. }