Java中使用JCOM操作Office對象

來源:互聯網
上載者:User

  通過使用COM技術,我們用微軟Office應用程式能夠建立很多應用程式擴充,但是Java開發人員卻無法享受它帶來的便利--除非他們擁有方便的Java訪問COM的途徑(Java-to-COM橋)。使用JCom的時候,你可以在Java中控制幾乎所有的COM對象,而且它還帶有一些用於Excel的強大的輔助類。

  在你每次編寫用HTML表格樣式或Java表格對象顯示資料的應用程式的時候,通常都需要帶有"匯出到Excel"功能。那麼頭疼的問題就出現了。怎麼樣實現這種功能呢?在HTML中顯示的可以在Office 2003中處理嗎?沒有這麼好!你還必須支援Office 97!

  你只能去找一個符合當前需求的工具了,但是接著收到更多的要求了。"這能在Word中做到嗎?Powerpoint能做到嗎?能不能用數據機撥號到遠程伺服器上並發布資料?Java無法實現這些功能是什麼意思啊?Java可以實現任何功能。"

  感謝作為Java和COM橋樑的架構組件,它使你在遇到這些情況的時候都可以回答"Yes"。Java-COM橋樑使你能夠根據自己的需要操作Windows組件--以前這是VB、C++和.NET開發人員的領地。你通過實現一個與DCOM後端(back end)對話的Java前端(front end),可以遠離端對端(end-to-end)的COM系統。在本文的末尾,你可以使用其中一個Java-to-COM橋:它可以被命名為JCom。

  Excel基礎知識

  開始之前,你需要首先從Sourceforge網站下載API。它包含了JCom所使用的Java類的所有原始碼、C++代碼和JCom用於配置Java和COM的編譯好的DLL。把這個DLL放到你的Java主目錄的/bin/目錄下面,否則會出現問題。同時,為了不出現問題,還要正確地設定JAVA_HOME環境變數。JCom的大多數文檔目前都是用日語寫的,但是翻譯工作進行中中,因此以後會有些改進的。

  下載和安裝過程完成以後,用列表1中的代碼試一試。這段代碼會建立到Excel的JCom介面,並把"Hello World"寫入第一個儲存格中。你可以看到1所示的結果。儘管JCom是一個通用的COM類庫,但是還是帶有很多用於Excel的輔助類,這是因為Excel可能是最常用的自動化COM應用程式。這些輔助類可以為我們節省很多時間,它們使JCOM成為一個更好的類庫了。

  列表1:開始使用JCOM和Excel

  import jp.ne.so_net.ga2.no_ji.jcom.excel8.*;
  import jp.ne.so_net.ga2.no_ji.jcom.*;

  public class testSimple
  {
  public static void main(String[] args) throws Exception {
  ReleaseManager rm = new ReleaseManager();
  try {
  System.out.println("EXCEL is Starting...");
  ExcelApplication excel = new ExcelApplication(rm);
  excel.Visible(true);
  ExcelWorkbooks xlBooks = excel.Workbooks();
  ExcelWorkbook xlBook = xlBooks.Add();
  ExcelWorksheets xlSheets = xlBook.Worksheets();
  ExcelWorksheet xlSheet = xlSheets.Item(1);
  ExcelRange xlRange = xlSheet.Cells();
  xlRange.Item(1,1).Value("Hello, World!" );
  }
  catch(Exception e) { e.printStackTrace(); }
  finally { rm.release(); }
  }
  }

  圖1:Java中使用Excel的第一個COM自動化

  如果曾經使用VB或VBA來自動化Excel,那麼你應該很熟悉列表1中的代碼了。ExcelApplication類暴露了Workbooks()屬性,它列舉出Excel中當前開啟的工作薄(workbook),並允許你向啟動並執行Excel副本增加或刪除工作薄。使用.Add()方法增加工作薄,這個方法會返回一個工作薄的引用。

  在Excel中工作薄用於儲存輸入的資料。工作薄是XLS檔案的基礎。一個工作薄由多個工作表(worksheet,在圖1中有三個工作表,分別叫做Sheet1、Sheet2和Sheet3)。工作表通過Item屬性來枚舉。你可以使用這個屬性訪問特定的工作表。在圖1中,Sheet 1是第一個工作表,因此你可以使用xlSheets.Item(1)來擷取對它的引用。

  有了工作表之後,你就可以使用range(範圍)來操作它上面的資料。Range是一個儲存格或多個儲存格。例如,儲存格A1可以使用range (1,1)來引用,接著可以使用Value屬性把資料載入儲存格中。

   更有意義的例子

  假設你希望把Excel作為資料庫中某些資料的表現層。在Java中你希望通過JDBC擷取資料,並把資料顯示在Excel前端。這種假設是很好的,因為複雜商務邏輯中的用例(use case)已經用Excel最上層顯示了,更不用說工作流程中的其它的資料項目的顯示和互動操作了。現在你不需要瞭解商務邏輯或分析,就可以使用原始的試算表,並使用Java中的自動化來"填充它們之間的裂痕"。

  我將給出一個示範這種操作的簡單樣本:本文下載中所包含的內容是建立比較銷售行為的一個簡單的MySQL資料庫的。它有三個表:

  · Sales是銷售的細節資訊,包括銷售項、數量、銷售價格、是誰銷售的以及銷售地區。

  · People包含銷售人員的姓名和傭金。

  · Districts包含了銷售地區的名稱和稅率。

  產生報表的時候,你需要使用下面的商務邏輯來算出真正的數值:

  · 毛銷售額等於銷售項乘以銷售價格加上地稅金額。

  · 純銷售額等於毛銷售額減去銷售人員提取的稅前傭金。

  這都是一些在Java中可以實現的簡單直接的計算過程,但是我在本文中使用它們的目的是示範如何把這些資料寫入Excel試算表並讓Excel自動計算。對於更複雜的情況(使用了更複雜的Excel公式),原理也是一樣的。

  此外,下載的內容中還包含了2所示的試算表。它是作為"模板"供你填充適當的資料的試算表。

  圖2:用於前端填充資料的Excel模板

  使用JCOM的時候,查詢資料庫中所有銷售資料需要使用下面的SQL:

  SELECT sales.id, sales.description, sales.quantity, sales.price, districts.districtname,districts.salestax,people.name,people.commission
  FROM 'sales','districts','people'
  WHERE (sales.district = districts.id) AND (sales.salesperson = people.id)

   接著使用自動化(automation)把每條記錄都寫入Excel試算表的適當的列中。下載的內容中包含了完整的代碼(列表2所示)。在下面的部分,我將為你解釋代碼中使用JCOM的自動化部分。

  列表2:從資料庫中擷取資料並載入Excel中的代碼

  // 首先建立Excel的引用
  ExcelApplication excel = new ExcelApplication(rm);
  // 使它可視
  excel.Visible(true);
  // 接著開啟我們將使用的模板工作薄
  ExcelWorkbooks xlBooks = excel.Workbooks();
  ExcelWorkbook xlBook = xlBooks.Open("c:\sales.xls");
  // 接著擷取我們將修改的範圍的引用
  ExcelWorksheets xlSheets = xlBook.Worksheets();
  ExcelWorksheet xlSheet = xlSheets.Item(1);
  ExcelRange xlRange = xlSheet.Cells();

  …
  // 此處放置資料庫連結和查詢建立代碼
  …

  if (stmt.execute(strSql))
  {
  rs = stmt.getResultSet();
  }
  int nColCount = rs.getMetaData().getColumnCount();
  int nRow=1;
  int nCol=0;
  // 在記錄集中迴圈
  while(rs.next())
  {
  // 記錄集中的每行都是試算表中的一行
  nRow++;
  for(int i=1;i<=nColCount;i++)
  {
  // 匹配DB列和試算表列
  nCol = getExcelColumn(i);
  // 尋找於行和列對應的儲存格,並把它設定為適當的記錄集欄位
  xlRange.Item(nRow,nCol).Value(rs.getObject(i).toString());
  }
  // 填入公式
  xlRange.Item(nRow,7).Value("=RC[-2]*RC[-1]");
  xlRange.Item(nRow,9).Value("=RC[-2]*RC[-1]/100");
  xlRange.Item(nRow,10).Value("=RC[-3]+RC[-1]");
  xlRange.Item(nRow,11).Value("=RC[-4]*((100-RC[2])/100)");
  }

  在列表2的第一部分中,其目標是擷取希望修改的儲存格的控制權。這會花費了一定的開銷。

  · 首先你必須擷取表現Excel本身的對象,有了JCOM輔助類的協助以後,這一步操作相當直接。

  ExcelApplication excel = new ExcelApplication(rm);
  
  · 下一步,你希望擷取對工作薄集合的訪問權。你希望開啟自己的模板工作薄(本樣本中這個模板在C:sales.xls中),在工作薄集合中開啟它。

  ExcelWorkbooks xlBooks = excel.Workbooks();

  · 下一步,你希望開啟自己的工作薄並擷取該工作薄集合的引用。

  ExcelWorkbook xlBook = xlBooks.Open("c:\sales.xls");
  ExcelWorksheets xlSheets = xlBook.Worksheets();

  · 最後,你希望擷取集合中的第一個工作表,並把工作範圍定義為整個工作表。

  ExcelWorksheet xlSheet = xlSheets.Item(1);
  ExcelRange xlRange = xlSheet.Cells();

  完成這些工作以後,你的xlRange對象將允許你在單元中放入值和公式了。通過在前面的記錄集(從提交給資料庫的SQL命令中返回的)中迴圈,使用xlRange.Item(nRow,nCol).Value("whatever")文法,逐行逐列地把記錄集中的值插入到試算表中(文法中的whatever來自於記錄集)。通過使用rs.getObject(i).toString可以得到當前行中I列的值。通過使用rs.movenext(),當前行一直向後移動,直到記錄集的末尾。

  你在列表2中可能會注意到一個奇怪的函數調用:

  nCol = getExcelColumn(i);

  這是一個簡單的輔助函數,它把記錄集中的列編號與Excel中存放資料的位置進行匹配。例如,如果你再次查看上面的SQL語句,傭金費率將返回到第8列。在試算表中,存放它的列是第13列。這個函數用於處理兩者之間的轉換。更複雜的應用程式可以使用Excel中的命名(named)範圍來匹配列,但是這超出了本文的範圍。

   最後,應用程式把公式輸入適當的單元中。它是使用Excel的R1C1符號來實現的,因此相應的字串是用於描述公式的。在Excel中,假設你希望把第1行中A列的值與B列的值相加,然後放到C列中,那麼你就需要在C列中輸入公式"=A1+B1"。如果在第二行你也希望這樣操作,那麼可以複製和粘貼這個公式,它會自動地更新為"=A2+B2"。

  從樣本程式中得到這些值需要一些字串操作,但是如果使用"R1C1"相關的轉換就非常直接了。在本文的例子中,你在C1中輸入公式的時候,不需要給出"=A1+B1"引用,而是給出"=RC[-2]+RC[-1]"引用,它的意思是把向前兩列的值與向前一列的值相加。

  當你移動了其它行的時候,這個公式的值不會發生改變,因此它使我們產生程式所需要的值非常簡單了。這也是我用於計算總銷售額、銷售稅金、毛銷售額和純銷售額的方法,如下所示:

  xlRange.Item(nRow,7).Value("=RC[-2]*RC[-1]");
  xlRange.Item(nRow,9).Value("=RC[-2]*RC[-1]/100");
  xlRange.Item(nRow,10).Value("=RC[-3]+RC[-1]");
  xlRange.Item(nRow,11).Value("=RC[-4]*((100-RC[2])/100)");

  運行這段代碼將得到圖3所示的試算表。



  圖3:公式:Excel顯示了資料庫查詢的輸出資訊

  請注意,這是一種比較好的工程學經驗,即不要在應用程式中包含公式,因為公式可能隨時改變,而你肯定不希望在改變公式的時候就去改變自己的代碼。這種辦法使每個新資料行成為包含公式的已有資料行的副本。模板試算表(sales.xls)包含了假資料,並在第二列中包含了公式(第一列包含了欄位標題)。因此,當你填充每行的時候,在你填入資料庫值的之前線複製這一行的內容。通過這種辦法,公式通過複製進入了新資料行,並且你不需要在Java代碼中包含任何商務邏輯。

  我們看一看salesReport2.java檔案(也在下載內容中)中的內容,你可以發現,我們並沒有使用前面所說的公式,而是在While迴圈頂部使用了下面的代碼:

  if(nRow>1)
  {
  String strDest="A"+(nRow+1);
  ExcelRange xlRange2Copy = xlSheet.Range("A2:M2");
  xlRange2Copy.Copy(xlSheet.Range(strDest));
  }

  除了第一行之外(第一行不需要處理,這樣的資訊已經包含在sales.xls中了),這段代碼將處理A2到M2之間的單元,並把它們複製到nRow+1標識的資料行中(nRow是當前行的編號)。加上1的原因是計算了欄位標題(欄位標題在第一行)。

  超越Excel

  請記住JCOM是用於COM的而不僅僅是用於Excel的。你可以使用這種工具在Java中控制幾乎所有的COM對象。Excel是最常見的被控制對象,因為有很多很好的輔助類可以讓它更簡單,但是所有的COM組件都是可以使用的。因此控制其它的應用程式(例如Word和Powerpoint)也是直接的。

  下面是使用Word的一個例子:

  IDispatch wdApp = new IDispatch(rm, "Word.Application");
  wdApp.put("Visible", new Boolean(true));

  由於沒有用於Word的直接的輔助類,每個部分都必須通過使用IDispatch對象來實現(用於Excel的下層輔助類也是這樣實現的)。上面的代碼將載入微軟Word副本,並通過wdApp對象使它可以自動化操作。接著你可以使用類似"put"(設定屬性)或"method"(調用方法)等方法來使用Word。例如,上面的代碼把Visible屬性設定為true,使得該Word應用程式可視。

  如果你一定要使用Windows,那麼有大量的COM組件可以擴充你的視野,它的數量比Java目前提供的多很多。通過串列和並行口進行硬體控制也可以在Java中實現了,而這僅僅是個開始。在你的工具包中有了JCOM之後,就不受任何限制了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.