jxl和poi處理excel之比較


     功能需求是根據客戶提供的excel模板,程序動態填充其中的一些數據。該模板包含大量的宏,剛拿到的時候頭都要暈了,本人雖天天和電腦打交道,但是excel咱不是高手啊,什么宏,之前光聽過,聽着都覺得是高級的東西,就暈,但作為富有責任心的程序員,咱得攻克,也真的攻克了,現在鄙人去搞財務也沒問題啊。

之前看各種評論說poi普遍對excel的支持更多一些,比較好用,具體jxl不好用在哪里,沒有具體的說法,我就以為無傷大雅,而且項目之前的一些功能都是用jxl的,我就繼續沿用jxl。首先來說下流程:

1.根據excel模板創建新工作簿,本質就是copy一份新的,在新的上面進行后續操作。

2.讀取其中兩個sheet頁,對其中內容進行修改。

3.設置密碼保護。

4.給第一個sheet頁設置選中狀態。

5.寫出excel文件,關閉流之類的。

下面直接貼代碼,這都是瀝心之作啊,一筆一畫都是心血,程序猿虧心虧腦啊,天氣炎熱,得好好休養。

首先是變量,這玩意是我自己項目需要,貼出來只為大家看代碼看的明白,沒啥意思的哈。

 int colRefNo = 8;        //RefNo所在列,列數是從0開始
 int colVari = 9;      //Variables所在列
 int colChannel = 10;  //Channel所在列
 
 int colAFYP= 11;  //AFYP所在列
 int colNBSP= 12;    //NBSP所在列
 int colVONB= 14;   //VONB所在列
 
 int channelNum = 8; //excel模板channel的個數 
 int channelPart = 35;  //每個channel的部分占多少行
 int startRow = 18;    //excel模板上數據的起始行,行數從0開始的

先貼jxl,斷斷續續搞了近一個月,中間端午回家,要不是因為它,我完全可以毫無牽掛地休上半個月到一個月的,說起都是淚啊。

 public String modifyExcel(){
  String infilepath = "c:/AA.xls";//模板文件位置
  String filePath = "c:/test/";
  String fileName = "jxlFile.xls";
  String targetfile = filePath + fileName;//生成文件位置
  File file = new File(targetfile);
  //判斷文件夾是否存在,不存在就創建
  File parent = file.getParentFile();
  if (parent != null && !parent.exists()) {
   parent.mkdirs();
  }
  //創建新工作簿
  Workbook rw = null;
  WritableWorkbook  wwb = null;
  
  try {
   rw = Workbook.getWorkbook(new File(infilepath)); //讀入excel模板
  } catch (Exception e) {
   e.printStackTrace();
   return e.getMessage();
  }
  try {
   wwb = Workbook.createWorkbook(file, rw); //根據現有只讀文件創建一個可寫入的Excel工作薄對象
  } catch (Exception e) {
   e.printStackTrace();
   return e.getMessage();
  }
  //讀取第一張隱藏的工作表,取消隱藏屬性
  WritableSheet ws0 = wwb.getSheet(0);  
  ws0.setHidden(true);
  System.out.println("===ws0.isHidden()="+ws0.isHidden()+"==ws0.isProtected()==="+ws0.isProtected());
  
  WritableSheet wsPP = wwb.getSheet(2);    //讀取第三張工作表,PP
  
  WritableSheet wsNP = wwb.getSheet(3);   //讀取第四張工作表,NP
   
  wsPP = modifySheet(wsPP,"PP");   //對PP修改內容
  wsNP = modifySheet(wsNP,"NP");   //對NP修改內容
  
  SheetSettings ssPP = wsPP.getSettings();//設置PP的密碼保護
  ssPP.setPassword("ABCDEFG");
  ssPP.setProtected(true);
  
  SheetSettings ssNP = wsNP.getSettings();//設置NP的密碼保護
  ssNP.setPassword("ABCDEFG");
  ssNP.setProtected(true);
  
  ssNP.setSelected(false);
  //讀取第二張的工作表,設置選中狀態
  WritableSheet ws1 = wwb.getSheet(1);  
  ws1.getSettings().setSelected(true);
  
  //寫入Excel對象
  try {
   wwb.write();
  } catch (Exception e) {
   return e.getMessage();
  }
  //關閉可寫入的Excel對象
  try {
   wwb.close();
  } catch (Exception e) {
   e.printStackTrace();
   return e.getMessage();
  }
  //關閉只讀的Excel對象
  rw.close();  
  return "文件"+fileName+"已生成在"+filePath+"下!";
 }

 private WritableSheet modifySheet(WritableSheet ws,String pnp) {
  
  int start = startRow;
  DailySalesBean dsb = new DailySalesBean();
  dsb.setPnp(pnp);

  for (int j= 0 ; j< channelNum; j++){
   int end = start+channelPart;
   for(int i= start;i< end;i++){

    Cell cellRefNo = ws.getCell(colRefNo, i);
    Cell cellVari = ws.getCell(colVari, i);
    Cell cellChannel = ws.getCell(colChannel, i);
    //獲取excel中某些列的值作為查詢條件
    dsb.setRefno(ObjectUtil.isEmpty(cellRefNo.getContents())?"":cellRefNo.getContents());
    dsb.setVari(ObjectUtil.isEmpty(cellVari.getContents())?"":cellVari.getContents());
    dsb.setChannel(ObjectUtil.isEmpty(cellChannel.getContents())?"":cellChannel.getContents());
    //根據條件得出數據結果
    DailySalesBean dsb2 = getDailyData(dsb);

    WritableCell afypCell = ws.getWritableCell(colAFYP,i);
    if(afypCell.getType() == CellType.NUMBER ){//單元格的數據格式,保證是number類型,涉及到自動計算
     Number afyp = (Number)afypCell;
     afyp.setValue(Double.parseDouble(dsb2.getAfyp()));//修改單元格的數值
    }
    WritableCell nbspCell = ws.getWritableCell(colNBSP,i);
    if(nbspCell.getType() == CellType.NUMBER ){
     Number nbsp = (Number)nbspCell;
     nbsp.setValue(Double.parseDouble(dsb2.getNbsp()));
    }
    WritableCell vonbCell = ws.getWritableCell(colVONB,i);
    if(vonbCell.getType() == CellType.NUMBER ){
     Number vonb = (Number)vonbCell;
     vonb.setValue(Double.parseDouble(dsb2.getVonb()));
    }
   //上面是獲取單元格,直接修改值。下面注釋掉的是另外一種方法,是new一個新的單元格,設置完值之后,add到sheet頁里面去,個人覺得有種多余,應該浪費資源會多一

//點。但是在將數據單元格改為字符串單元格,涉及到數據類型更改的時候,后一種方法就派上用場了。
/* CellFormat cf1 = null;
    CellFormat cf2 = null;
    CellFormat cf3 = null;
      if(j==0 && k==1 ){
     Cell afypCell_old = ws.getCell(colAFYP,i);
     Cell nbspCell_old = ws.getCell(colNBSP,i);
     Cell vonbCell_old = ws.getCell(colVONB,i);
     
     cf1 = afypCell_old.getCellFormat();
     cf2 = nbspCell_old.getCellFormat();
     cf3 = vonbCell_old.getCellFormat();//為保持原有單元格的樣式,譬如邊框,顏色,字體之類的,需要獲取原有樣式賦到新單元格上
    }
     Number afypCell = new Number(colAFYP, i, Double.parseDouble(dsb2.getAfyp()));
    afypCell.setCellFormat(cf1);
    try {
     ws.addCell(afypCell);
    } catch (Exception e) {
     e.printStackTrace();
    }
    
    Number nbspCell = new Number(colNBSP, i, Double.parseDouble(dsb2.getNbsp()));
    nbspCell.setCellFormat(cf2);
    try {
     ws.addCell(nbspCell);
    } catch (Exception e) {
     e.printStackTrace();
    }
    
    Number vonbCell = new Number(colVONB, i, Double.parseDouble(dsb2.getVonb()));
    vonbCell.setCellFormat(cf3);
    try {
     ws.addCell(vonbCell);
    } catch (Exception e) {
     e.printStackTrace();
    }
    */
   }
   start += channelPart;
  }
  return ws;
 }

再貼個簡潔的POI代碼:

 public String modifyExcel(){
  String infilepath = "c:/AA.xls";
  String filePath = "c:/Sales_Daily_java/";
  String fileNane = "InputTemplate-Sales_Daily_Draft.xls";
  String targetfile = filePath + fileNane;
  File file = new File(targetfile);
  //判斷文件夾是否存在,不存在就創建
  File parent = file.getParentFile();
  if (parent != null && !parent.exists()) {
   parent.mkdirs();
  }
  //創建新工作簿
  HSSFWorkbook wb = null;//創建97-03版excel,如果是07-10版,則XSSFWorkbook wb = null;
  NPOIFSFileSystem fs = null;
  try {
   fs = new NPOIFSFileSystem(new File(infilepath));
  } catch (Exception e1) {
   e1.printStackTrace();
   return "讀取excel模板文件失敗!";
  }
  try {
   wb = new HSSFWorkbook(fs.getRoot(),true);
  } catch (Exception e1) {
   e1.printStackTrace();
   return "創建excel文件失敗!";
  }

  //讀取第三張工作表,PP_DailyInput
   Sheet spp = wb.getSheetAt(2);
  //讀取第四張工作表,NP_DailyInput
   Sheet snp = wb.getSheetAt(3);
   
  spp = modifySheet(spp,"PP");   //對PP_DailyInput修改內容
  snp = modifySheet(snp,"NP");   //對NP_DailyInput修改內容
  
  spp.protectSheet("XDFZALDEXCAVPGS");//設置PP_DailyInput Sheet的密碼保護
  snp.protectSheet("XDFZALDEXCAVPGS");//設置NP_DailyInput Sheet的密碼保護
  
  snp.setSelected(false);
  //讀取第二張的工作表,設置選中狀態
  Sheet s1 = wb.getSheetAt(1);
  s1.setSelected(true);
  
   //輸出流文件,關閉流
  FileOutputStream fileOut = null;
  try {
   fileOut = new FileOutputStream(targetfile);
   wb.write(fileOut);
   fileOut.flush();
   fileOut.close();
  } catch (Exception e) {
   e.printStackTrace();
   return "excel文件生成失敗!";
  }
  try {
   fs.close();  //一定要比上面的后關閉,不然write的時候會拋異常,文件寫出不了
  } catch (IOException e1) {
   e1.printStackTrace();
  }
  return "文件"+fileNane+"已生成在"+filePath+"下!";
 }
 /**
  * 獲取單元格的值
  * @param cell
  * @return
  */
 private String getCellValue(Cell cell){
  String result = null;
  switch (cell.getCellType()) {
  
         case Cell.CELL_TYPE_STRING:
          
          result = cell.getRichStringCellValue().getString();
          break;
         case Cell.CELL_TYPE_NUMERIC:
          
             if (DateUtil.isCellDateFormatted(cell)) {
        result = cell.getDateCellValue().toString();
             } else {
              Double cellValue = cell.getNumericCellValue();
              DecimalFormat df = new DecimalFormat("#.#");
              result = df.format(cellValue);
             }
             break;
         case Cell.CELL_TYPE_BOOLEAN:
          
    result = Boolean.toString(cell.getBooleanCellValue());
          break;
         case Cell.CELL_TYPE_FORMULA:
          
    result = cell.getCellFormula();
             break;
         default:
          result ="";
     }
  if(StringUtils.isEmpty(result)){
   result = "";
  }
  return result;
 }
 private Sheet modifySheet(Sheet ws,String pnp) {
  
  int start = startRow;
  DailySalesBean dsb = new DailySalesBean();
  dsb.setPnp(pnp);

  for (int j= 0 ; j< channelNum; j++){
   int end = start+channelPart;
   for(int i= start;i< end;i++){
    Row row = ws.getRow(i);
    Cell cellRefNo = row.getCell(colRefNo);
    Cell cellVari = row.getCell(colVari);
    Cell cellChannel = row.getCell(colChannel);
    
    dsb.setRefno(getCellValue(cellRefNo));
    dsb.setVari(getCellValue(cellVari));
    dsb.setChannel(getCellValue(cellChannel));
    
    DailySalesBean dsb2 = getDailyData(dsb);
    
    Cell afypCell = row.getCell(colAFYP);
    afypCell.setCellValue(Double.parseDouble(dsb2.getAfyp()));
    
    Cell nbspCell = row.getCell(colNBSP);
    nbspCell.setCellValue(Double.parseDouble(dsb2.getNbsp()));
    
    Cell vonbCell = row.getCell(colVONB);
    vonbCell.setCellValue(Double.parseDouble(dsb2.getVonb()));
   }
   start += channelPart;
  }
  ws.setForceFormulaRecalculation(true);//excel中有寫計算公式,數據修改之后強制重新調用里面的公式自動計算
  return ws;
 }

現在來總結下吧,之所以最后UAT發布關頭改變方法,是因為jxl生成出來的excel在用office2010或07打開的時候總彈出來倆數據丟失和數據格式丟失的alert框,這玩意給客戶感覺得是多不好啊,而且客戶給的excel模板比較惡心,貌似因為宏的原因隱藏了個sheet頁,一打開的時候下面的sheet頁切換不靈光,而且原有的sheet設置的tab顏色也丟了。如果用office2003打開,那就是直接都打不開了,一打開就直接excel停止工作。琢磨了三四天,各種資料查下來都說是excel各種版本不兼容之類的問題,大家混亂的解決方案下來我也沒解決問題。痛心之下抱着死馬當活馬醫的心態,在發布UAT的前一天晚上查詢poi的相關資料,對比了下jxl和poi的不同寫法,第二天一早動筆修改程序,換血用poi重寫代碼,那邊在如火如荼地發布,解決發布過程中的各種問題,我這邊淡定地寫着代碼,終於趕在他們解決完問題之前寫完了代碼,中午寫完,測試的時候,心情那個忐忑啊,要是還不行,我估計就得撞牆去了。顫抖着手,咽着快餐,啟動服務器,測試,生成,生成的excel完美,和模板一模一樣,激動的我呀,小心肝都快飛出來了。沒有alert框,sheet切換正常,tab顏色也在,perfect!!!經此一事,忠實擁戴poi,看了下poi的api,齊全的,各種office操作的東西,poi都有相應的方法可供調用。

參考文獻:http://poi.apache.org/spreadsheet/quick-guide.html#Iterator,著名的Busy Developers' Guide to HSSF and XSSF Features,新手必看,比看API塊

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM