1. 程式人生 > >POI事件驅動模式讀寫Excel和格式設定及2007EXCEL解析SAXParser類找不到

POI事件驅動模式讀寫Excel和格式設定及2007EXCEL解析SAXParser類找不到

POI事件驅動模式讀寫Excel

目前處理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。

/**
 * 抽象Excel2003讀取器,通過實現HSSFListener監聽器,採用事件驅動模式解析excel2003
 * 中的內容,遇到特定事件才會觸發,大大減少了記憶體的使用。
 * 注意Excel2003Reader類中引用的Record類在hssf包下  
 */
public  class Excel2003Reader implements HSSFListener{
 private int minColumns = -1;
 private POIFSFileSystem fs;
 private int lastRowNumber;
 private int lastColumnNumber;

 /** Should we output the formula, or the value it has? */
 private boolean outputFormulaValues = true;

 /** For parsing Formulas */
 private SheetRecordCollectingListener workbookBuildingListener;
 //excel2003工作薄
 private HSSFWorkbook stubWorkbook;

 // Records we pick up as we process
 private SSTRecord sstRecord;
 private FormatTrackingHSSFListener formatListener;

 //表索引
 private int sheetIndex = -1;
 private BoundSheetRecord[] orderedBSRs;
 @SuppressWarnings("unchecked")
 private ArrayList boundSheetRecords = new ArrayList();

 // For handling formulas with string results
 private int nextRow;
 private int nextColumn;
 private boolean outputNextStringRecord;
 //當前行
 private int curRow = 0;
 //儲存行記錄的容器
 private List<String> rowlist = new ArrayList<String>();;
 @SuppressWarnings( "unused")
 private String sheetName;
 
 private IRowReader rowReader;

 
 public void setRowReader(IRowReader rowReader){
  this.rowReader = rowReader;
 }
 
 /**
  * 遍歷excel下所有的sheet
  * @throws IOException
  */
 public void process(String fileName) throws IOException {
  this.fs = new POIFSFileSystem(new FileInputStream(fileName));
  MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(
    this);
  formatListener = new FormatTrackingHSSFListener(listener);
  HSSFEventFactory factory = new HSSFEventFactory();
  HSSFRequest request = new HSSFRequest();
  if (outputFormulaValues) {
   request.addListenerForAllRecords(formatListener);
  } else {
   workbookBuildingListener = new SheetRecordCollectingListener(
     formatListener);
   request.addListenerForAllRecords(workbookBuildingListener);
  }
  factory.processWorkbookEvents(request, fs);
 }
 
 /**
  * HSSFListener 監聽方法,處理 Record
  */
 @SuppressWarnings("unchecked")
 public void processRecord(Record record) {
  int thisRow = -1;
  int thisColumn = -1;
  String thisStr = null;
  String value = null;
  switch (record.getSid()) {
   case BoundSheetRecord.sid:
    boundSheetRecords.add(record);
    break;
   case BOFRecord.sid:
    BOFRecord br = (BOFRecord) record;
    if (br.getType() == BOFRecord.TYPE_WORKSHEET) {
     // 如果有需要,則建立子工作薄
     if (workbookBuildingListener != null && stubWorkbook == null) {
      stubWorkbook = workbookBuildingListener
        .getStubHSSFWorkbook();
     }
     
     sheetIndex++;
     if (orderedBSRs == null) {
      orderedBSRs = BoundSheetRecord
        .orderByBofPosition(boundSheetRecords);
     }
     sheetName = orderedBSRs[sheetIndex].getSheetname();
    }
    break;
 
   case SSTRecord.sid:
    sstRecord = (SSTRecord) record;
    break;
 
   case BlankRecord.sid:
    BlankRecord brec = (BlankRecord) record;
    thisRow = brec.getRow();
    thisColumn = brec.getColumn();
    thisStr = "";
    rowlist.add(thisColumn, thisStr);
    break;
   case BoolErrRecord.sid: //單元格為布林型別
    BoolErrRecord berec = (BoolErrRecord) record;
    thisRow = berec.getRow();
    thisColumn = berec.getColumn();
    thisStr = berec.getBooleanValue()+"";
    rowlist.add(thisColumn, thisStr);
    break;
 
   case FormulaRecord.sid: //單元格為公式型別
    FormulaRecord frec = (FormulaRecord) record;
    thisRow = frec.getRow();
    thisColumn = frec.getColumn();
    if (outputFormulaValues) {
     if (Double.isNaN(frec.getValue())) {
      // Formula result is a string
      // This is stored in the next record
      outputNextStringRecord = true;
      nextRow = frec.getRow();
      nextColumn = frec.getColumn();
     } else {
      thisStr = formatListener.formatNumberDateCell(frec);
     }
    } else {
     thisStr = '"' + HSSFFormulaParser.toFormulaString(stubWorkbook,
       frec.getParsedExpression()) + '"';
    }
    rowlist.add(thisColumn,thisStr);
    break;
   case StringRecord.sid://單元格中公式的字串
    if (outputNextStringRecord) {
     // String for formula
     StringRecord srec = (StringRecord) record;
     thisStr = srec.getString();
     thisRow = nextRow;
     thisColumn = nextColumn;
     outputNextStringRecord = false;
    }
    break;
   case LabelRecord.sid:
    LabelRecord lrec = (LabelRecord) record;
    curRow = thisRow = lrec.getRow();
    thisColumn = lrec.getColumn();
    value = lrec.getValue().trim();
    value = value.equals("")?" ":value;
    this.rowlist.add(thisColumn, value);
    break;
   case LabelSSTRecord.sid:  //單元格為字串型別
    LabelSSTRecord lsrec = (LabelSSTRecord) record;
    curRow = thisRow = lsrec.getRow();
    thisColumn = lsrec.getColumn();
    if (sstRecord == null) {
     rowlist.add(thisColumn, " ");
    } else {
     value =  sstRecord
     .getString(lsrec.getSSTIndex()).toString().trim();
     value = value.equals("")?" ":value;
     rowlist.add(thisColumn,value);
    }
    break;
   case NumberRecord.sid:  //單元格為數字型別
    NumberRecord numrec = (NumberRecord) record;
    curRow = thisRow = numrec.getRow();
    thisColumn = numrec.getColumn();
    value = formatListener.formatNumberDateCell(numrec).trim();
    value = value.equals("")?" ":value;
    // 向容器加入列值
    rowlist.add(thisColumn, value);
    break;
   default:
    break;
  }

  // 遇到新行的操作
  if (thisRow != -1 && thisRow != lastRowNumber) {
   lastColumnNumber = -1;
  }

  // 空值的操作
  if (record instanceof MissingCellDummyRecord) {
   MissingCellDummyRecord mc = (MissingCellDummyRecord) record;
   curRow = thisRow = mc.getRow();
   thisColumn = mc.getColumn();
   rowlist.add(thisColumn," ");
  }

  // 更新行和列的值
  if (thisRow > -1)
   lastRowNumber = thisRow;
  if (thisColumn > -1)
   lastColumnNumber = thisColumn;

  // 行結束時的操作
  if (record instanceof LastCellOfRowDummyRecord) {
   if (minColumns > 0) {
    // 列值重新置空
    if (lastColumnNumber == -1) {
     lastColumnNumber = 0;
    }
   }
   lastColumnNumber = -1;
    // 每行結束時, 呼叫getRows() 方法
   rowReader.getRows(sheetIndex,curRow, rowlist);
   
   // 清空容器
   rowlist.clear();
  }
 }
 
}

/**
 * 抽象Excel2007讀取器,excel2007的底層資料結構是xml檔案,採用SAX的事件驅動的方法解析
 * xml,需要繼承DefaultHandler,在遇到檔案內容時,事件會觸發,這種做法可以大大降低
 * 記憶體的耗費,特別使用於大資料量的檔案。
 * 注意Excel2007Reader類中引用的Attributes在org.xml.sal包下,Date在java.util包下
 */
public class Excel2007Reader extends DefaultHandler {
 //共享字串表
 private SharedStringsTable sst;
 //上一次的內容
 private String lastContents;
 private boolean nextIsString;

 private int sheetIndex = -1;
 private List<String> rowlist = new ArrayList<String>();
 //當前行
 private int curRow = 0;
 //當前列
 private int curCol = 0;
 //日期標誌
 private boolean dateFlag;
 //數字標誌
 private boolean numberFlag;
 
 private boolean isTElement;
 
 private IRowReader rowReader;
 
 public void setRowReader(IRowReader rowReader){
  this.rowReader = rowReader;
 }
 
 /**只遍歷一個電子表格,其中sheetId為要遍歷的sheet索引,從1開始,1-3
  * @param filename
  * @param sheetId
  * @throws Exception
  */
 public void processOneSheet(String filename,int sheetId) throws Exception {
  OPCPackage pkg = OPCPackage.open(filename);
  XSSFReader r = new XSSFReader(pkg);
  SharedStringsTable sst = r.getSharedStringsTable();
  XMLReader parser = fetchSheetParser(sst);
  
  // 根據 rId# 或 rSheet# 查詢sheet
  InputStream sheet2 = r.getSheet("rId"+sheetId);
  sheetIndex++;
  InputSource sheetSource = new InputSource(sheet2);
  parser.parse(sheetSource);
  sheet2.close();
 }

 /**
  * 遍歷工作簿中所有的電子表格
  * @param filename
  * @throws Exception
  */
 public void process(String filename) throws Exception {
  OPCPackage pkg = OPCPackage.open(filename);
  XSSFReader r = new XSSFReader(pkg);
  SharedStringsTable sst = r.getSharedStringsTable();
  XMLReader parser = fetchSheetParser(sst);
  Iterator<InputStream> sheets = r.getSheetsData();
  while (sheets.hasNext()) {
   curRow = 0;
   sheetIndex++;
   InputStream sheet = sheets.next();
   InputSource sheetSource = new InputSource(sheet);
   parser.parse(sheetSource);
   sheet.close();
  }
 }

 public XMLReader fetchSheetParser(SharedStringsTable sst)
   throws SAXException {
  XMLReader parser = XMLReaderFactory
    .createXMLReader("org.apache.xerces.parsers.SAXParser");
  this.sst = sst;
  parser.setContentHandler(this);
  return parser;
 }

 public void startElement(String uri, String localName, String name,
   Attributes attributes) throws SAXException {
  
  // c => 單元格
  if ("c".equals(name)) {
   // 如果下一個元素是 SST 的索引,則將nextIsString標記為true
   String cellType = attributes.getValue("t");
   if ("s".equals(cellType)) {
    nextIsString = true;
   } else {
    nextIsString = false;
   }
   //日期格式
   String cellDateType = attributes.getValue("s");
   if ("1".equals(cellDateType)){
    dateFlag = true;
   } else {
    dateFlag = false;
   }
   String cellNumberType = attributes.getValue("s");
   if("2".equals(cellNumberType)){
    numberFlag = true;
   } else {
    numberFlag = false;
   }
   
  }
  //當元素為t時
  if("t".equals(name)){
   isTElement = true;
  } else {
   isTElement = false;
  }
  
  // 置空
  lastContents = "";
 }

 public void endElement(String uri, String localName, String name)
   throws SAXException {
  
  // 根據SST的索引值的到單元格的真正要儲存的字串
  // 這時characters()方法可能會被呼叫多次
  if (nextIsString) {
   try {
    int idx = Integer.parseInt(lastContents);
    lastContents = new XSSFRichTextString(sst.getEntryAt(idx))
      .toString();
   } catch (Exception e) {

   }
  }
  //t元素也包含字串
  if(isTElement){
   String value = lastContents.trim();
   rowlist.add(curCol, value);
   curCol++;
   isTElement = false;
   // v => 單元格的值,如果單元格是字串則v標籤的值為該字串在SST中的索引
   // 將單元格內容加入rowlist中,在這之前先去掉字串前後的空白符
  } else if ("v".equals(name)) {
   String value = lastContents.trim();
   value = value.equals("")?" ":value;
   //日期格式處理
   if(dateFlag){
     Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));
     SimpleDateFormat dateFormat = new SimpleDateFormat(
                 "dd/MM/yyyy");
     value = dateFormat.format(date);
   }
   //數字型別處理
   if(numberFlag){
    BigDecimal bd = new BigDecimal(value);
    value = bd.setScale(3,BigDecimal.ROUND_UP).toString();
   }
   rowlist.add(curCol, value);
   curCol++;
  }else {
   //如果標籤名稱為 row ,這說明已到行尾,呼叫 optRows() 方法
   if (name.equals("row")) {
    rowReader.getRows(sheetIndex,curRow,rowlist);
    rowlist.clear();
    curRow++;
    curCol = 0;
   }
  }
  
 }

 public void characters(char[] ch, int start, int length)
   throws SAXException {
  //得到單元格內容的值
  lastContents += new String(ch, start, length);
 }
}

public class ExcelReaderUtil {
 
 //excel2003副檔名
 public static final String EXCEL03_EXTENSION = ".xls";
 //excel2007副檔名
 public static final String EXCEL07_EXTENSION = ".xlsx";
 
 /**
  * 讀取Excel檔案,可能是03也可能是07版本
  * @param excel03
  * @param excel07
  * @param fileName
  * @throws Exception
  */
 public static void readExcel(IRowReader reader,String fileName) throws Exception{
  // 處理excel2003檔案
  if (fileName.endsWith(EXCEL03_EXTENSION)){
   Excel2003Reader excel03 = new Excel2003Reader();
   excel03.setRowReader(reader);
   excel03.process(fileName);
  // 處理excel2007檔案
  } else if (fileName.endsWith(EXCEL07_EXTENSION)){
   Excel2007Reader excel07 = new Excel2007Reader();
   excel07.setRowReader(reader);
   excel07.process(fileName);
  } else {
   throw new  Exception("檔案格式錯誤,fileName的副檔名只能是xls或xlsx。");
  }
 }
}

public interface IRowReader {
 
 /**業務邏輯實現方法
  * @param sheetIndex
  * @param curRow
  * @param rowlist
  */
 public  void getRows(int sheetIndex,int curRow, List<String> rowlist);
}

public class RowReader implements IRowReader{


 /* 業務邏輯實現方法
  * @see com.eprosun.util.excel.IRowReader#getRows(int, int, java.util.List)
  */
 public void getRows(int sheetIndex, int curRow, List<String> rowlist) {
  // TODO Auto-generated method stub
  System.out.print(curRow+" ");
  for (int i = 0; i < rowlist.size(); i++) {
   System.out.print(rowlist.get(i) + " ");
  }
  System.out.println();
 }

}

public class Main {
 
 public static void main(String[] args) throws Exception {
  IRowReader reader = new RowReader();
  //ExcelReaderUtil.readExcel(reader, "F://te2003.xls");
  ExcelReaderUtil.readExcel(reader, "F://test2007.xlsx");
 }
}

/**
* 注意Excel2003Writer類中引用的Sheet在poi.ss.usermodel包下.
*/
public class Excel2003Writer {

 /**
  * @param args
  */
 public static void main(String[] args) {
  try{ 
   System.out.println("開始寫入excel2003....");
   writeExcel("tes2003.xls");
   System.out.println("寫完xcel2003");
  } catch (IOException e) {
  
  }
 }
 
 
 /**
  * 寫入excel並填充內容,一個sheet只能寫65536行以下,超出會報異常,寫入時建議使用AbstractExcel2007Writer
  * @param fileName
  * @throws IOException
  */
 public static void writeExcel(String fileName) throws IOException{
   
   // 建立excel2003物件
   Workbook wb = new HSSFWorkbook();
   
   // 設定檔案放置路徑和檔名
      FileOutputStream fileOut = new FileOutputStream(fileName);
      // 建立新的表單
      Sheet sheet = wb.createSheet("newsheet");
      // 建立新行
      for(int i=0;i<20000;i++){
       Row row = sheet.createRow(i);
       // 建立單元格
       Cell cell = row.createCell(0);
       // 設定單元格值
       cell.setCellValue(1);
       row.createCell(1).setCellValue(1+i);
       row.createCell(2).setCellValue(true);
       row.createCell(3).setCellValue(0.43d);
       row.createCell(4).setCellValue('d');
       row.createCell(5).setCellValue("");
       row.createCell(6).setCellValue("第七列"+i);
       row.createCell(7).setCellValue("第八列"+i);
      }
      wb.write(fileOut);
      fileOut.close();
 }


}

/**
 * 抽象excel2007讀入器,先構建.xlsx一張模板,改寫模板中的sheet.xml,使用這種方法
 * 寫入.xlsx檔案,不需要太大的記憶體
 * 注意AbstractExcel2007Writer類中引用的CellReference在org.apache.poi.ss.util/org.apache.poi.hssf.util包下需確認下
 */
public abstract class AbstractExcel2007Writer {
 
 private SpreadsheetWriter sw;

 /**
  * 寫入電子表格的主要流程
  * @param fileName
  * @throws Exception
  */
 public void process(String fileName) throws Exception{
  // 建立工作簿和電子表格物件
  XSSFWorkbook wb = new XSSFWorkbook();
  XSSFSheet sheet = wb.createSheet("sheet1");
  // 持有電子表格資料的xml檔名 例如 /xl/worksheets/sheet1.xml
  String sheetRef = sheet.getPackagePart().getPartName().getName();

  // 儲存模板
  FileOutputStream os = new FileOutputStream("template.xlsx");
  wb.write(os);
  os.close();
  
  // 生成xml檔案
  File tmp = File.createTempFile("sheet", ".xml");
  Writer fw = new FileWriter(tmp);
  sw = new SpreadsheetWriter(fw);
  generate();
  fw.close();
  
  // 使用產生的資料替換模板
  File templateFile = new File("template.xlsx");
  FileOutputStream out = new FileOutputStream(fileName);
  substitute(templateFile, tmp, sheetRef.substring(1), out);
  out.close();
  //刪除檔案之前呼叫一下垃圾回收器,否則無法刪除模板檔案
  System.gc();
  // 刪除臨時模板檔案
  if (templateFile.isFile()&&templateFile.exists()){
   templateFile.delete();
  }
 }

 /**
  * 類使用者應該使用此方法進行寫操作
  * @throws Exception
  */
 public abstract void generate() throws Exception;

 public void beginSheet() throws IOException {
  sw.beginSheet();
 }

 public void insertRow(int rowNum) throws IOException {
  sw.insertRow(rowNum);
 }

 public void createCell(int columnIndex, String value) throws IOException {
  sw.createCell(columnIndex, value, -1);
 }

 public void createCell(int columnIndex, double value) throws IOException {
  sw.createCell(columnIndex, value, -1);
 }

 public void endRow() throws IOException {
  sw.endRow();
 }

 public void endSheet() throws IOException {
  sw.endSheet();
 }

 /**
  *
  * @param zipfile the template file
  * @param tmpfile the XML file with the sheet data
  * @param entry the name of the sheet entry to substitute, e.g. xl/worksheets/sheet1.xml
  * @param out the stream to write the result to
  */
 private static void substitute(File zipfile, File tmpfile, String entry,
   OutputStream out) throws IOException {
  ZipFile zip = new ZipFile(zipfile);
  ZipOutputStream zos = new ZipOutputStream(out);

  @SuppressWarnings("unchecked")
  Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries();
  while (en.hasMoreElements()) {
   ZipEntry ze = en.nextElement();
   if (!ze.getName().equals(entry)) {
    zos.putNextEntry(new ZipEntry(ze.getName()));
    InputStream is = zip.getInputStream(ze);
    copyStream(is, zos);
    is.close();
   }
  }
  zos.putNextEntry(new ZipEntry(entry));
  InputStream is = new FileInputStream(tmpfile);
  copyStream(is, zos);
  is.close();
  zos.close();
 }

 private static void copyStream(InputStream in, OutputStream out)
   throws IOException {
  byte[] chunk = new byte[1024];
  int count;
  while ((count = in.read(chunk)) >= 0) {
   out.write(chunk, 0, count);
  }
 }

 /**
  * 在寫入器中寫入電子表格
  *
  */
 public static class SpreadsheetWriter {
  private final Writer _out;
  private int _rownum;
  private static String LINE_SEPARATOR = System.getProperty("line.separator");

  public SpreadsheetWriter(Writer out) {
   _out = out;
  }

  public void beginSheet() throws IOException {
   _out.write("<?xml version=\"1.0\" encoding=\"GB2312\"?>"
       + "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");
   _out.write("<sheetData>"+LINE_SEPARATOR);
  }

  public void endSheet() throws IOException {
   _out.write("</sheetData>");
   _out.write("</worksheet>");
  }

  /**
   * 插入新行
   *
   * @param rownum 以0開始
   */
  public void insertRow(int rownum) throws IOException {
   _out.write("<row r=\"" + (rownum + 1) + "\">"+LINE_SEPARATOR);
   this._rownum = rownum;
  }

  /**
   * 插入行結束標誌
   */
  public void endRow() throws IOException {
   _out.write("</row>"+LINE_SEPARATOR);
  }

  /**
   * 插入新列
   * @param columnIndex
   * @param value
   * @param styleIndex
   * @throws IOException
   */
  public void createCell(int columnIndex, String value, int styleIndex)
    throws IOException {
   String ref = new CellReference(_rownum, columnIndex)
     .formatAsString();
   _out.write("<c r=\"" + ref + "\" t=\"inlineStr\"");
   if (styleIndex != -1)
    _out.write(" s=\"" + styleIndex + "\"");
   _out.write(">");
   _out.write("<is><t>"+XMLEncoder.encode(value)+"</t></is>");
   _out.write("</c>");
  }

  public void createCell(int columnIndex, String value)
    throws IOException {
   createCell(columnIndex, value, -1);
  }

  public void createCell(int columnIndex, double value, int styleIndex)
    throws IOException {
   String ref = new CellReference(_rownum, columnIndex)
     .formatAsString();
   _out.write("<c r=\"" + ref + "\" t=\"n\"");
   if (styleIndex != -1)
    _out.write(" s=\"" + styleIndex + "\"");
   _out.write(">");
   _out.write("<v>" + value + "</v>");
   _out.write("</c>");
  }

  public void createCell(int columnIndex, double value)
    throws IOException {
   createCell(columnIndex, value, -1);
  }

  public void createCell(int columnIndex, Calendar value, int styleIndex)
    throws IOException {
   createCell(columnIndex, DateUtil.getExcelDate(value, false),
     styleIndex);
  }
 }
}

public class Excel2007WriterImpl extends AbstractExcel2007Writer{

 
 /**
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  // TODO Auto-generated method stub
  System.out.println("............................");
  long start = System.currentTimeMillis();
  //構建excel2007寫入器
  AbstractExcel2007Writer excel07Writer = new Excel2007WriterImpl();
  //呼叫處理方法
  excel07Writer.process("F://test07.xlsx");
  long end = System.currentTimeMillis();
  System.out.println("....................."+(end-start)/1000);
 }

 
 /*
  * 可根據需求重寫此方法,對於單元格的小數或者日期格式,會出現精度問題或者日期格式轉化問題,建議使用字串插入方法
  * @see com.excel.ver2.AbstractExcel2007Writer#generate()
  */
 @Override
 public void generate()throws Exception {
        //電子表格開始
        beginSheet();
        for (int rownum = 0; rownum < 100; rownum++) {
         //插入新行
            insertRow(rownum);
            //建立新單元格,索引值從0開始,表示第一列
            createCell(0, "中國<" + rownum + "!");
            createCell(1, 34343.123456789);
            createCell(2, "23.67%");
            createCell(3, "12:12:23");
            createCell(4, "2010-10-11 12:12:23");
            createCell(5, "true");
            createCell(6, "false");
         
            //結束行
            endRow();
        }
        //電子表格結束
        endSheet();
 }

}

public class XMLEncoder {

    private static final String[] xmlCode = new String[256];

    static {
        // Special characters
        xmlCode['\''] = "'";
        xmlCode['\"'] = "\""; // double quote
        xmlCode['&'] = "&"; // ampersand
        xmlCode['<'] = "<"; // lower than
        xmlCode['>'] = ">"; // greater than
    }

    /**
     * <p>
     * Encode the given text into xml.
     * </p>
     *
     * @param string the text to encode
     * @return the encoded string
     */
    public static String encode(String string) {
        if (string == null) return "";
        int n = string.length();
        char character;
        String xmlchar;
        StringBuffer buffer = new StringBuffer();
        // loop over all the characters of the String.
        for (int i = 0; i < n; i++) {
            character = string.charAt(i);
            // the xmlcode of these characters are added to a StringBuffer one by one
            try {
                xmlchar = xmlCode[character];
                if (xmlchar == null) {
                    buffer.append(character);
                } else {
                    buffer.append(xmlCode[character]);
                }
            } catch (ArrayIndexOutOfBoundsException aioobe) {
                buffer.append(character);
            }
        }
        return buffer.toString();
    }

}

需要匯入的jar包參考如下:

注意以上程式還需要引入以下jar包
xmlbeans_2.3.0.jar,dom4j.jar,xercesImpl.jar

WorkbookFactory 找不到類問題

在最近的POI版本中,poi-3.7.jar包中找不到WorkbookFactory這個類,該類在建立一個存在的Excel檔案並讀取內容時會用到,而這個類存在於:poi-ooxml-3.7.jar中,因此如果在apapche網站下載poi包時,需要引入這兩個jar包;

POI匯入匯出Excel資料簡單例子

一、準備

筆者使用:JDK1.5 + POI 3.6,本程式碼既支援Excel2003又支援Excel2007。

需要匯入的jar包:

Excel 資料檔案:

姓名 性別 國籍 學號 年齡 專業 入學日期
陳美嘉 中國 05412578 24 電腦科學與技術 2009/9/1
陸展博 中國 05412579 24 電腦科學與技術 2009/9/2
關穀神奇 日本 05412580 24 電腦科學與技術 2009/9/3
張偉 中國 05412581 24 電腦科學與技術 2009/9/4
林宛瑜 中國 05412582 24 電腦科學與技術 2009/9/5
曾小賢 中國 05412583 24 電腦科學與技術 2009/9/6
呂子喬 韓國 05412584 24 電腦科學與技術 2009/9/7
胡一菲 中國 05412585 24 電腦科學與技術 2009/9/8

二、程式碼

Bean:

建立bean物件,bean物件儲存資料。

[java] view plaincopyprint?
  1. package com.test;  
  2. /** 
  3.  *  
  4.  * Student Bean 
  5.  *  
  6.  */
  7. publicclass Student  
  8. {  
  9. private String name;  
  10. private String sex;  
  11. private String age;  
  12. private String country;  
  13. private String studentID;  
  14. private String specialty;  
  15. private String admissionTime;  
  16. public String getName()  
  17.     {  
  18. return name;  
  19.     }  
  20. publicvoid setName(String name)  
  21.     {  
  22. this.name = name;  
  23.     }  
  24. public String getSex()  
  25.     {  
  26. return sex;  
  27.     }  
  28. publicvoid setSex(String sex)  
  29.     {  
  30. this.sex = sex;  
  31.     }  
  32. public String getAge()  
  33.     {  
  34. return age;  
  35.     }  
  36. publicvoid setAge(String age)  
  37.     {  
  38. this.age = age;  
  39.     }  
  40. public String getCountry()  
  41.     {  
  42. return country;  
  43.     }  
  44. publicvoid setCountry(String country)  
  45.     {  
  46. this.country = country;  
  47.     }  
  48. public String getStudentID()  
  49.     {  
  50. return studentID;  
  51.     }  
  52. publicvoid setStudentID(String studentID)  
  53.     {  
  54. this.studentID = studentID;  
  55.     }  
  56. public String getSpecialty()  
  57.     {  
  58. return specialty;  
  59.     }  
  60. publicvoid setSpecialty(String specialty)  
  61.     {  
  62. this.specialty = specialty;  
  63.     }  
  64. public String getAdmissionTime()  
  65.     {  
  66. return admissionTime;  
  67.     }  
  68. publicvoid setAdmissionTime(String admissionTime)  
  69.     {  
  70. this.admissionTime = admissionTime;  
  71.     }  
  72. @Override
  73. public String toString()  
  74.     {  
  75.         StringBuffer sb = new StringBuffer();  
  76.         sb.append("[name=" + this.name + "]")  
  77.             .append(",")  
  78.             .append("[sex=" + this.sex + "]")  
  79.             .append(",")  
  80.             .append("[age=" + this.age + "]")  
  81.             .append(",")  
  82.             .append("[country=" + this.country + "]")  
  83.             .append(",")  
  84.             .append("[studentID=" + this.studentID + "]")  
  85.             .append(",")  
  86.             .append("[specialty=" + this.specialty + "]")  
  87.             .append(",")  
  88.             .append("[admissionTime=" + this.admissionTime + "]");  
  89. return sb.toString();  
  90.     }  
  91. }  
package com.test;
/**
 * 
 * Student Bean
 * 
 */
public class Student
{
    private String name;
    
    private String sex;
    
    private String age;
    
    private String country;
    
    private String studentID;
    
    private String specialty;
    
    private String admissionTime;
    
    public String getName()
    {
        return name;
    }
    
    public void setName(String name)
    {
        this.name = name;
    }
    
    public String getSex()
    {
        return sex;
    }
    
    public void setSex(String sex)
    {
        this.sex = sex;
    }
    
    public String getAge()
    {
        return age;
    }
    
    public void setAge(String age)
    {
        this.age = age;
    }
    
    public String getCountry()
    {
        return country;
    }
    
    public void setCountry(String country)
    {
        this.country = country;
    }
    
    public String getStudentID()
    {
        return studentID;
    }
    
    public void setStudentID(String studentID)
    {
        this.studentID = studentID;
    }
    
    public String getSpecialty()
    {
        return specialty;
    }
    
    public void setSpecialty(String specialty)
    {
        this.specialty = specialty;
    }
    
    public String getAdmissionTime()
    {
        return admissionTime;
    }
    
    public void setAdmissionTime(String admissionTime)
    {
        this.admissionTime = admissionTime;
    }
    
    @Override
    public String toString()
    {
        StringBuffer sb = new StringBuffer();
        sb.append("[name=" + this.name + "]")
            .append(",")
            .append("[sex=" + this.sex + "]")
            .append(",")
            .append("[age=" + this.age + "]")
            .append(",")
            .append("[country=" + this.country + "]")
            .append(",")
            .append("[studentID=" + this.studentID + "]")
            .append(",")
            .append("[specialty=" + this.specialty + "]")
            .append(",")
            .append("[admissionTime=" + this.admissionTime + "]");
        return sb.toString();
    }
    
}

匯入操作類:

1、使用WorkbookFactory.create方法將Excel資料檔案載入到工廠中,根據不同的檔案型別(2007還是2003)得到不同的Workbook。

2、呼叫Workbook.getSheetAt(0),獲取第一個工作簿。

3、獲取總行數,根據總行數迭代獲取每一行(Row表示行)的資料。

4、使用Row的getCell獲取資料,獲取資料時需要注意獲取的每一列的資料型別,根據不同的資料型別轉換成字串。

5、將列中資料一一對應封裝到bean物件中。

[java] view plaincopyprint?
  1. package com.test;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.text.SimpleDateFormat;  
  8. import java.util.ArrayList;  
  9. import java.util.Date;  
  10. import java.util.List;  
  11. import org.apache.poi.hssf.usermodel.HSSFCell;  
  12. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;  
  13. import org.apache.poi.ss.usermodel.Cell;  
  14. import org.apache.poi.ss.usermodel.DateUtil;  
  15. import org.apache.poi.ss.usermodel.Row;  
  16. import org.apache.poi.ss.usermodel.Sheet;  
  17. import org.apache.poi.ss.usermodel.Workbook;  
  18. import org.apache.poi.ss.usermodel.WorkbookFactory;  
  19. /** 
  20.  *  
  21.  * 操作Excel表格的功能類 
  22.  *  
  23.  */
  24. publicfinalclass ExcelReader  
  25. {  
  26. /** 
  27.      * 讀取Excel資料內容 
  28.      * @param excelDataFile 待解析的檔案 
  29.      * @return List<Student> 學生集合 
  30.      */
  31. publicstatic List<Student> readExcelContent(File excelDataFile)  
  32.     {  
  33.         List<Student> students = new ArrayList<Student>();  
  34.         Workbook workbook = null;  
  35. // Partner Excel 資料檔案流
  36.         InputStream inputStream = null;  
  37. try
  38.         {  
  39.             inputStream = new FileInputStream(excelDataFile);  
  40.             workbook = WorkbookFactory.create(inputStream);  
  41.         }  
  42. catch (FileNotFoundException e)  
  43.         {  
  44.             e.printStackTrace();  
  45.         }  
  46. catch (IOException e)  
  47.         {  
  48.             e.printStackTrace();  
  49.         }  
  50. catch (InvalidFormatException e)  
  51.         {  
  52.             e.printStackTrace();  
  53.         }  
  54. finally
  55.         {  
  56. try
  57.             {  
  58. if (null != inputStream)  
  59.                 {  
  60.                     inputStream.close();  
  61.                 }  
  62.             }  
  63. catch (IOException e)  
  64.             {  
  65.                 e.printStackTrace();  
  66.             }  
  67.         }  
  68. // 獲取第一個工作簿
  69.         Sheet sheet = workbook.getSheetAt(0);  
  70. //得到總行數
  71. int rowNum = sheet.getLastRowNum();  
  72. // 資料行
  73.         Row row = null;  
  74. //正文內容應該從第二行開始,第一行為表頭的標題
  75. for (int i = 1; i <= rowNum; i++)  
  76.         {  
  77. // 獲取第二行(內容行)
  78.             row = sheet.getRow(i);  
  79. if (null != row)  
  80.             {  
  81.                 students.add(buildStudent(row));  
  82.             }  
  83.         }  
  84. return students;  
  85.     }  
  86. /** 
  87.      * 構建Partner物件 
  88.      * @param row Excel資料行 
  89.      * @return 構建好的Student物件 
  90.      * @see [類、類#