Jacob處理Word文件總結以及Java操作Office2007
使用Jacob來處理Word文件
Word或Excel程式是以一種COM元件形式存在的。如果能夠在Java中呼叫Word的COM元件,就
能使用它的方法來獲取Word文件中的文字資訊。目前網上有許多提供這樣的工具。
1 Jacob的下載
Jacob 是Java-COM Bridge的縮寫,它在Java與微軟的COM元件之間構建一座橋樑。使用Jacob
自帶的DLL動態連結庫,並通過JNI的方式實現了在Java平臺上對COM程式的呼叫。Jacob下載
採用的是jacob_1.11_zip。解壓下載的 Jacob_1.11_zip檔案後,
2 在Eclipse中配置
(1)將jacob.jar匯入工程的Build Path,然後確認自己機器的CPU型別(X86或AMD64),並
選擇不同目錄下的jacob.dll檔案。
(2)將jacob.dll放到% JAVA_HOME%/jre/bin目錄下,其中,%JAVA_HOME%就是JDK的安裝
目錄。注意這個的jre目錄必須是Eclipse當前正在使 用的目錄,在Eclipse中選擇“window-
>Preferences”選單,在彈出的對話方塊中選擇“Java->Installed JREs”項,
(3)當前選擇的JRE是“C:/Program Files/Java/jdk1.5.0_07/jre”目錄下的,所以需要把
jacob.dll複製到“C:/Program Files/Java/jdk1.5.0_07/jre/bin”目錄下面。
(4)在工程中新建一個ch7.jacob包, 並在包中建立WordReader類。該類將提供一個靜態的
extractDoc()方法。它接收兩個引數,一個是要處理的DOC檔名,另一個則是輸出 的檔名
,然後通過JNI呼叫Word的API轉換內容,該函式的程式碼如下。
public static void extractDoc(String inputFIle, String outputFile) {
boolean flag = false;
// 開啟Word應用程式
ActiveXComponent app = new ActiveXComponent("Word.Application");
try {
// 設定word不可見
app.setProperty("Visible", new Variant(false));
// 開啟word檔案
Dispatch doc1 = app.getProperty("Documents").toDispatch();
Dispatch doc2 = Dispatch.invoke(
doc1,
"Open",
Dispatch.Method,
new Object[] { inputFIle, new Variant(false),
new Variant(true) }, new int[1]).toDispatch();
// 作為txt格式儲存到臨時檔案
Dispatch.invoke(doc2, "SaveAs", Dispatch.Method, new Object[] {
outputFile, new Variant(7) }, new int[1]);
// 關閉word
Variant f = new Variant(false);
Dispatch.call(doc2, "Close", f);
flag = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
app.invoke("Quit", new Variant[] {});
}
if (flag == true) {
System.out.println("Transformed Successfully");
} else {
System.out.println("Transform Failed");
}
}
(5)建立一個main函式來測試WordReader類,該main函式程式碼如下。
public static void main(String[] args) {
WordReader.extractDoc("c:/test.doc","c:/jacob.txt");
}
(6)新生成的txt檔案被儲存到c:/jacob.txt下
在使用Jacob時,很重要的一點是,使用者本地系統中必須安裝有Word的應用程式。否則也就無
法建立Java-COM橋,進而無法解析了。
解決jacob呼叫word處理doc檔案的記憶體溢位問題
背景:
有個專案在系統後臺用Jacob呼叫Word程序的API做doc檔案處理。在進行壓力測試的時候,發現執行一段時間後,記憶體佔用奇高,大約7.4G。由於JVM佔用記憶體限制為2G,因此懷疑多出來的記憶體應當是Jacob洩露的。
在網路上搜索解決辦法,都是要這樣呼叫
[java] view plaincopyprint?ComThread.InitSTA();
// do something
ComThread.Release();
ComThread.InitSTA();
// do something
ComThread.Release();
但是在專案中,使用了執行緒池進行DOC檔案處理,也就是同一時間,會有多個WORD程序在跑,使用ComThread.InitSTA();之後,Jacob僅允許執行緒池裡面的一個執行緒執行,其他執行緒都被鎖住。
最後修改成ComThread.InitMTA(true);來初始化,經過24小時壓力測試,可以同時有多個WORD程序執行,另外也解決了Jacob記憶體溢位問題。
使用JACOB進行Word程式設計示例
import java.util.ArrayList;
import java.util.List;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.apache.log4j.Logger;
/**
*
*作用:利用jacob外掛生成word 檔案!
*
*
*/
public class DOCWriter {
/** 日誌記錄器 */
static private Logger logger = Logger.getLogger(DOCWriter.class);
/** word文件
*
* 在本類中有兩種方式可以進行文件的建立,<br>
* 第一種呼叫 createNewDocument
* 第二種呼叫 openDocument
*/
private Dispatch document = null;
/** word執行程式物件 */
private ActiveXComponent word = null;
/** 所有word文件 */
private Dispatch documents = null;
/**
* Selection 物件 代表視窗或窗格中的當前所選內容。 所選內容代表文件中選定(或突出
顯示)的區域,如果文件中沒有選定任何內容,則代表插入點。
* 每個文件窗格只能有一個Selection 物件,並且在整個應用程式中只能有一個活動的
Selection 物件。
*/
private Dispatch selection = null;
/**
*
* Range 物件 代表文件中的一個連續區域。 每個 Range 物件由一個起始字元位置和一個
終止字元位置定義。
* 說明:與書籤在文件中的使用方法類似,Range 物件在 Visual Basic 過程中用來標識文
檔的特定部分。
* 但與書籤不同的是,Range物件只在定義該物件的過程執行時才存在。
* Range物件獨立於所選內容。也就是說,您可以定義和處理一個範圍而無需更改所選內
容。還可以在文件中定義多個範圍,但每個窗格中只能有一個所選內容。
*/
private Dispatch range = null;
/**
* PageSetup 物件 該物件包含文件的所有頁面設定屬性(如左邊距、下邊距和紙張大小)
。
*/
private Dispatch pageSetup = null;
/** 文件中的所有表格物件 */
private Dispatch tables = null;
/** 一個表格物件 */
private Dispatch table = null;
/** 表格所有行物件 */
private Dispatch rows = null;
/** 表格所有列物件 */
private Dispatch cols = null;
/** 表格指定行物件 */
private Dispatch row = null;
/** 表格指定列物件 */
private Dispatch col = null;
/** 表格中指定的單元格 */
private Dispatch cell = null;
/** 字型 */
private Dispatch font = null;
/** 對齊方式 */
private Dispatch alignment = null;
/**
* 構造方法
*/
public DOCWriter() {
if(this.word == null){
/* 初始化應用所要用到的物件例項 */
this.word = new ActiveXComponent("Word.Application");
/* 設定Word文件是否可見,true-可見false-不可見 */
this.word.setProperty("Visible",new Variant(true));
/* 禁用巨集 */
this.word.setProperty("AutomationSecurity", new Variant(3));
}
if(this.documents == null){
this.documents = word.getProperty("Documents").toDispatch();
}
}
/**
* 設定頁面方向和頁邊距
*
* @param orientation
* 可取值0或1,分別代表橫向和縱向
* @param leftMargin
* 左邊距的值
* @param rightMargin
* 右邊距的值
* @param topMargin
* 上邊距的值
* @param buttomMargin
* 下邊距的值
*/
public void setPageSetup(int orientation, int leftMargin,int rightMargin, int topMargin, int
buttomMargin) {
logger.debug("設定頁面方向和頁邊距...");
if(this.pageSetup == null){
this.getPageSetup();
}
Dispatch.put(pageSetup, "Orientation", orientation);
Dispatch.put(pageSetup, "LeftMargin", leftMargin);
Dispatch.put(pageSetup, "RightMargin", rightMargin);
Dispatch.put(pageSetup, "TopMargin", topMargin);
Dispatch.put(pageSetup, "BottomMargin", buttomMargin);
}
/**
* 開啟檔案
*
* @param inputDoc
* 要開啟的檔案,全路徑
* @return Dispatch
* 開啟的檔案
*/
public Dispatch openDocument(String inputDoc) {
logger.debug("開啟Word文件...");
this.document = Dispatch.call(documents,"Open",inputDoc).toDispatch();
this.getSelection();
this.getRange();
this.getAlignment();
this.getFont();
this.getPageSetup();
return this.document;
}
/**
* 建立新的檔案
*
* @return Dispache 返回新建檔案
*/
public Dispatch createNewDocument(){
logger.debug("建立新的檔案...");
this.document = Dispatch.call(documents,"Add").toDispatch();
this.getSelection();
this.getRange();
this.getPageSetup();
this.getAlignment();
this.getFont();
return this.document;
}
/**
* 選定內容
* @return Dispatch 選定的範圍或插入點
*/
public Dispatch getSelection() {
logger.debug("獲取選定範圍的插入點...");
this.selection = word.getProperty("Selection").toDispatch();
return this.selection;
}
/**
* 獲取當前Document內可以修改的部分<p><br>
* 前提條件:選定內容必須存在
*
* @param selectedContent 選定區域
* @return 可修改的物件
*/
public Dispatch getRange() {
logger.debug("獲取當前Document內可以修改的部分...");
this.range = Dispatch.get(this.selection, "Range").toDispatch();
return this.range;
}
/**
* 獲得當前文件的文件頁面屬性
*/
public Dispatch getPageSetup() {
logger.debug("獲得當前文件的文件頁面屬性...");
if(this.document == null){
logger.warn("document物件為空...");
return this.pageSetup;
}
this.pageSetup = Dispatch.get(this.document, "PageSetup").toDispatch();
return this.pageSetup;
}
/**
* 把選定內容或插入點向上移動
* @param count 移動的距離
*/
public void moveUp(int count) {
logger.debug("把選定內容或插入點向上移動...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveUp");
}
}
/**
* 把選定內容或插入點向下移動
* @param count 移動的距離
*/
public void moveDown(int count) {
logger.debug("把選定內容或插入點向下移動...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveDown");
}
}
/**
* 把選定內容或插入點向左移動
* @param count 移動的距離
*/
public void moveLeft(int count) {
logger.debug("把選定內容或插入點向左移動...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveLeft");
}
}
/**
* 把選定內容或插入點向右移動
* @param count 移動的距離
*/
public void moveRight(int count) {
logger.debug("把選定內容或插入點向右移動...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveRight");
}
}
/**
* 回車鍵
*/
public void enterDown(int count){
logger.debug("按回車鍵...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection, "TypeParagraph");
}
}
/**
* 把插入點移動到檔案首位置
*/
public void moveStart() {
logger.debug("把插入點移動到檔案首位置...");
Dispatch.call(this.selection,"HomeKey",new Variant(6));
}
/**
* 從選定內容或插入點開始查詢文字
* @param selection 選定內容
* @param toFindText 要查詢的文字
* @return boolean true-查詢到並選中該文字,false-未查詢到文字
*/
public boolean find(String toFindText) {
logger.debug("從選定內容或插入點開始查詢文字"+" 要查詢內容: "+toFindText);
/* 從selection所在位置開始查詢 */
Dispatch find = Dispatch.call(this.selection,"Find").toDispatch();
/* 設定要查詢的內容 */
Dispatch.put(find,"Text",toFindText);
/* 向前查詢 */
Dispatch.put(find,"Forward","True");
/* 設定格式 */
Dispatch.put(find,"Format","True");
/* 大小寫匹配 */
Dispatch.put(find,"MatchCase","True");
/* 全字匹配 */
Dispatch.put(find,"MatchWholeWord","True");
/* 查詢並選中 */
return Dispatch.call(find,"Execute").getBoolean();
}
/**
* 把選定內容替換為設定文字
* @param selection 選定內容
* @param newText 替換為文字
*/
public void replace(String newText) {
logger.debug("把選定內容替換為設定文字...");
/* 設定替換文字 */
Dispatch.put(this.selection,"Text",newText);
}
/**
* 全域性替換
* @param selection 選定內容或起始插入點
* @param oldText 要替換的文字
* @param replaceObj 替換為文字
*/
public void replaceAll(String oldText,Object replaceObj) {
logger.debug("全域性替換...");
/* 移動到檔案開頭 */
moveStart();
/* 表格替換方式 */
String newText = (String) replaceObj;
/* 圖片替換方式 */
if(oldText.indexOf("image") != -1 || newText.lastIndexOf(".bmp") != -1 ||
newText.lastIndexOf(".jpg") != -1 || newText.lastIndexOf(".gif") != -1){
while (find(oldText)) {
insertImage(newText);
Dispatch.call(this.selection,"MoveRight");
}
/* 正常替換方式 */
} else {
while (find(oldText)) {
replace(newText);
Dispatch.call(this.selection,"MoveRight");
}
}
}
/**
* 插入圖片
* @param selection 圖片的插入點
* @param imagePath 圖片檔案(全路徑)
*/
public void insertImage(String imagePath) {
logger.debug("插入圖片...");
Dispatch.call(this.selection, "TypeParagraph");
Dispatch.call(Dispatch.get(this.selection,"InLineShapes").toDispatch
(),"AddPicture",imagePath);
}
/**
* 合併表格
*
* @param selection 操作物件
* @param tableIndex 表格起始點
* @param fstCellRowIdx 開始行
* @param fstCellColIdx 開始列
* @param secCellRowIdx 結束行
* @param secCellColIdx 結束列
*/
public void mergeCell(int tableIndex, int fstCellRowIdx, int fstCellColIdx, int
secCellRowIdx, int secCellColIdx){
logger.debug("合併單元格...");
if(this.table == null){
logger.warn("table物件為空...");
return;
}
Dispatch fstCell = Dispatch.call(table, "Cell",new Variant(fstCellRowIdx), new Variant
(fstCellColIdx)).toDispatch();
Dispatch secCell = Dispatch.call(table, "Cell",new Variant(secCellRowIdx), new Variant
(secCellColIdx)).toDispatch();
Dispatch.call(fstCell, "Merge", secCell);
}
/**
* 想Table物件中插入數值<p>
* 引數形式:ArrayList<String[]>List.size()為表格的總行數<br>
* String[]的length屬性值應該與所建立的表格列數相同
*
* @param selection 插入點
* @param tableIndex 表格起始點
* @param list 資料內容
*/
public void insertToTable(List<String[]> list){
System.out.println("向Table物件中插入資料...");
logger.debug("向Table物件中插入資料...");
if(list == null || list.size() <= 0){
logger.warn("寫出資料集為空...");
return;
}
if(this.table == null){
logger.warn("table物件為空...");
return;
}
for(int i = 0; i < list.size(); i++){
String[] strs = list.get(i);
for(int j = 0; j<strs.length; j++){
/* 遍歷表格中每一個單元格,遍歷次數與所要填入的內容數量相同 */
Dispatch cell = this.getCell(i+1, j+1);
/* 選中此單元格 */
Dispatch.call(cell, "Select");
/* 寫出內容到此單元格中 */
Dispatch.put(this.selection, "Text", strs[j]);
/* 移動遊標到下一個位置 */
}
this.moveDown(1);
}
this.enterDown(1);
}
/**
* 在文件中正常插入文字內容
*
* @param selection 插入點
* @param list 資料內容
*/
public void insertToDocument(List<String> list){
logger.debug("向Document物件中插入資料...");
if(list == null || list.size() <= 0){
logger.warn("寫出資料集為空...");
return;
}
if(this.document == null){
logger.warn("document物件為空...");
return;
}
for(String str : list){
/* 寫出至word中 */
this.applyListTemplate(3, 2);
Dispatch.put(this.selection, "Text", str);
this.moveDown(1);
this.enterDown(1);
}
}
/**
* 建立新的表格
*
* @param selection 插入點
* @param document 文件物件
* @param rowCount 行數
* @param colCount 列數
* @param width 邊框數值 0淺色1深色
* @return 新建立的表格物件
*/
public Dispatch createNewTable(int rowCount, int colCount, int width){
logger.debug("建立新的表格...");
if(this.tables == null){
this.getTables();
}
this.getRange();
if(rowCount > 0 && colCount > 0){
this.table = Dispatch.call(this.tables,"Add",this.range,new Variant(rowCount),new
Variant(colCount),new Variant(width)).toDispatch();
}
/* 返回新建立表格 */
return this.table;
}
/**
* 獲取Document物件中的所有Table物件
*
* @return 所有Table物件
*/
public Dispatch getTables(){
logger.debug("獲取所有表格物件...");
if(this.document == null){
logger.warn("document物件為空...");
return this.tables;
}
this.tables = Dispatch.get(this.document, "Tables").toDispatch();
return this.tables;
}
/**
* 獲取Document中Table的數量
*
* @return 表格數量
*/
public int getTablesCount(){
logger.debug("獲取文件中表格數量...");
if(this.tables == null){
this.getTables();
}
return Dispatch.get(tables, "Count").getInt();
}
/**
* 獲取指定序號的Table物件
*
* @param tableIndex Table序列
* @return
*/
public Dispatch getTable(int tableIndex){
logger.debug("獲取指定表格物件...");
if(this.tables == null){
this.getTables();
}
if(tableIndex >= 0){
this.table = Dispatch.call(this.tables, "Item", new Variant(tableIndex)).toDispatch();
}
return this.table;
}
/**
* 獲取表格的總列數
*
* @return 總列數
*/
public int getTableColumnsCount() {
logger.debug("獲取表格總行數...");
if(this.table == null){
logger.warn("table物件為空...");
return 0;
}
return Dispatch.get(this.cols,"Count").getInt();
}
/**
* 獲取表格的總行數
*
* @return 總行數
*/
public int getTableRowsCount(){
logger.debug("獲取表格總行數...");
if(this.table == null){
logger.warn("table物件為空...");
return 0;
}
return Dispatch.get(this.rows,"Count").getInt();
}
/**
* 獲取表格列物件
*
* @return 列物件
*/
public Dispatch getTableColumns() {
logger.debug("獲取表格行物件...");
if(this.table == null){
logger.warn("table物件為空...");
return this.cols;
}
this.cols = Dispatch.get(this.table,"Columns").toDispatch();
return this.cols;
}
/**
* 獲取表格的行物件
*
* @return 總行數
*/
public Dispatch getTableRows(){
logger.debug("獲取表格總行數...");
if(this.table == null){
logger.warn("table物件為空...");
return this.rows;
}
this.rows = Dispatch.get(this.table,"Rows").toDispatch();
return this.rows;
}
/**
* 獲取指定表格列物件
*
* @return 列物件
*/
public Dispatch getTableColumn(int columnIndex) {
logger.debug("獲取指定表格行物件...");
if(this.cols == null){
this.getTableColumns();
}
if(columnIndex >= 0){
this.col = Dispatch.call(this.cols, "Item", new Variant(columnIndex)).toDispatch();
}
return this.col;
}
/**
* 獲取表格中指定的行物件
*
* @param rowIndex 行序號
* @return 行物件
*/
public Dispatch getTableRow(int rowIndex){
logger.debug("獲取指定表格總行數...");
if(this.rows == null){
this.getTableRows();
}
if(rowIndex >= 0){
this.row = Dispatch.call(this.rows, "Item", new Variant(rowIndex)).toDispatch();
}
return this.row;
}
/**
* 自動調整表格
*/
public void autoFitTable() {
logger.debug("自動調整表格...");
int count = this.getTablesCount();
for (int i = 0; i < count; i++) {
Dispatch table = Dispatch.call(tables, "Item", new Variant(i + 1)).toDispatch();
Dispatch cols = Dispatch.get(table, "Columns").toDispatch();
Dispatch.call(cols, "AutoFit");
}
}
/**
* 獲取當前文件中,表格中的指定單元格
*
* @param CellRowIdx 單元格所在行
* @param CellColIdx 單元格所在列
* @return 指定單元格物件
*/
public Dispatch getCell(int cellRowIdx, int cellColIdx) {
logger.debug("獲取當前文件中,表格中的指定單元格...");
if(this.table == null){
logger.warn("table物件為空...");
return this.cell;
}
if(cellRowIdx >= 0 && cellColIdx >=0){
this.cell = Dispatch.call(this.table, "Cell", new Variant(cellRowIdx),new Variant
(cellColIdx)).toDispatch();
}
return this.cell;
}
/**
* 設定文件標題
*
* @param title 標題內容
*/
public void setTitle(String title){
logger.debug("設定文件標題...");
if(title == null || "".equals(title)){
logger.warn("文件標題為空...");
return;
}
Dispatch.call(this.selection, "TypeText", title);
}
/**
* 設定當前表格線的粗細
*
* @param width
* width範圍:1<w<13,如果是0,就代表沒有框
*/
public void setTableBorderWidth(int width) {
logger.debug("設定當前表格線的粗細...");
if(this.table == null){
logger.warn("table物件為空...");
return;
}
/*
* 設定表格線的粗細 1:代表最上邊一條線 2:代表最左邊一條線 3:最下邊一條線 4
:最右邊一條線 5:除最上邊最下邊之外的所有橫線
* 6:除最左邊最右邊之外的所有豎線 7:從左上角到右下角的斜線 8:從左下角到右上
角的斜線
*/
Dispatch borders = Dispatch.get(table, "Borders").toDispatch();
Dispatch border = null;
for (int i = 1; i < 7; i++) {
border = Dispatch.call(borders, "Item", new Variant(i)).toDispatch();
if (width != 0) {
Dispatch.put(border, "LineWidth", new Variant(width));
Dispatch.put(border, "Visible", new Variant(true));
} else if (width == 0) {
Dispatch.put(border, "Visible", new Variant(false));
}
}
}
/**
* 對當前selection設定專案符號和編號
* @param tabIndex
* 1: 專案編號
* 2: 編號
* 3: 多級編號
* 4: 列表樣式
* @param index
* 0:表示沒有 ,其它數字代表的是該Tab頁中的第幾項內容
*/
public void applyListTemplate(int tabIndex,int index){
logger.debug("對當前selection設定專案符號和編號...");
/* 取得ListGalleries物件列表 */
Dispatch listGalleries = Dispatch.get(this.word, "ListGalleries").toDispatch();
/* 取得列表中一個物件 */
Dispatch listGallery = Dispatch.call(listGalleries, "Item", new Variant
(tabIndex)).toDispatch();
Dispatch listTemplates = Dispatch.get(listGallery, "ListTemplates").toDispatch();
if(this.range == null){
this.getRange();
}
Dispatch listFormat = Dispatch.get(this.range, "ListFormat").toDispatch();
Dispatch.call(listFormat,"ApplyListTemplate",Dispatch.call(listTemplates, "Item", new
Variant(index)), new Variant(true),new Variant(1),new Variant(0));
}
/**
* 增加文件目錄
*
* 目前採用固定引數方式,以後可以動態進行調整
*/
public void addTablesOfContents()
{
/* 取得ActiveDocument、TablesOfContents、range物件 */
Dispatch ActiveDocument = word.getProperty("ActiveDocument").toDispatch();
Dispatch TablesOfContents = Dispatch.get
(ActiveDocument,"TablesOfContents").toDispatch();
Dispatch range = Dispatch.get(this.selection, "Range").toDispatch();
/* 增加目錄 */
Dispatch.call(TablesOfContents,"Add",range,new Variant(true),new Variant(1),new
Variant(3),new Variant(true),new Variant(""),new Variant(true),new Variant(true));
}
/**
* 設定當前Selection 位置方式
* @param selectedContent 0-居左;1-居中;2-居右。
*/
public void setAlignment(int alignmentType) {
logger.debug("設定當前Selection 位置方式...");
if(this.alignment == null){
this.getAlignment();
}
Dispatch.put(this.alignment, "Alignment", alignmentType);
}
/**
* 獲取當前選擇區域的對齊方式
*
* @return 對其方式物件
*/
public Dispatch getAlignment(){
logger.debug("獲取當前選擇區域的對齊方式...");
if(this.selection == null){
this.getSelection();
}
this.alignment = Dispatch.get(this.selection, "ParagraphFormat").toDispatch();
return this.alignment;
}
/**
* 獲取字型物件
*
* @return 字型物件
*/
public Dispatch getFont(){
logger.debug("獲取字型物件...");
if(this.selection == null){
this.getSelection();
}
this.font = Dispatch.get(this.selection, "Font").toDispatch();
return this.font;
}
/**
* 設定選定內容的字型 注:在呼叫此方法前,選定區域物件selection必須存在
*
* @param fontName
* 字型名稱,例如 "宋體"
* @param isBold
* 粗體
* @param isItalic
* 斜體
* @param isUnderline
* 下劃線
* @param rgbColor
* 顏色,例如"255,255,255"
* @param fontSize
* 字型大小
* @param Scale
* 字元間距,百分比值。例如 70代表縮放為70%
*/
public void setFontScale(String fontName, boolean isBold, boolean isItalic, boolean
isUnderline, String rgbColor, int Scale, int fontSize) {
logger.debug("設定字型...");
Dispatch.put(this.font, "Name", fontName);
Dispatch.put(this.font, "Bold", isBold);
Dispatch.put(this.font, "Italic", isItalic);
Dispatch.put(this.font, "Underline", isUnderline);
Dispatch.put(this.font, "Color", rgbColor);
Dispatch.put(this.font, "Scaling", Scale);
Dispatch.put(this.font, "Size", fontSize);
}
/**
* 儲存檔案
* @param outputPath 輸出檔案(包含路徑)
*/
public void saveAs(String outputPath) {
logger.debug("儲存檔案...");
if(this.document == null){
logger.warn("document物件為空...");
return;
}
if(outputPath ==null || "".equals(outputPath)){
logger.warn("檔案儲存路徑為空...");
return;
}
Dispatch.call(this.document,"SaveAs",outputPath);
}
public void saveAsHtml(String htmlFile){
Dispatch.invoke(this.document,"SaveAs",Dispatch.Method, new Object[]{htmlFile,new
Variant(8)}, new int[1]);
}
/**
* 關閉檔案
* @param document 要關閉的檔案
*/
public void close() {
logger.debug("關閉檔案...");
if(document == null){
logger.warn("document物件為空...");
return;
}
Dispatch.call(document,"Close",new Variant(0));
}
/**
* 列印word檔案
*
*/
public void printFile(){
logger.debug("列印檔案...");
if(document == null){
logger.warn("document物件為空...");
return;
}
Dispatch.call(document,"PrintOut");
}
/**
* 退出程式
*/
public void quit() {
logger.debug("退出程式");
word.invoke("Quit",new Variant[0]);
ComThread.Release();
}
public static void main(String args[]){
DOCWriter writer = new DOCWriter();
writer.createNewDocument();
List<String[]> listTable = new ArrayList<String[]>();
for(int i = 0 ; i<10; i++){
String str[] = new String[4];
for(int j = 0 ; j<4; j++){
str[j] = String.valueOf(j);
}
listTable.add(str);
}
writer.setFontScale("宋體", false, false,false, "1,1,1,1", 70, 14);
List<String> list = new ArrayList<String>();
list.add("忘記你我做不到");
list.add("不去天涯海角");
list.add("在我身邊就好");
list.add("如果愛是痛苦的泥沼");
list.add("讓我們一起逃");
list.add("忘記你我做不到");
list.add("不去天涯海角");
list.add("在我身邊就好");
list.add("如果愛是痛苦的泥沼");
list.add("讓我們一起逃");
writer.moveDown(3);
writer.setAlignment(0);
writer.setTitle("Test");
writer.insertImage("E://test.jpg");
writer.enterDown(1);
writer.insertToDocument(list);
writer.setFontScale("幼圓", true, true,true, "1,1,1,1", 70, 14);
writer.createNewTable(10, 5, 0);
writer.insertToTable(listTable);
writer.setFontScale("華文仿宋", true, true,false, "1,1,1,1", 70, 14);
writer.createNewTable(10, 5, 0);
writer.insertToTable(listTable);
writer.setFontScale("華文新魏", true, false,false, "100,1,1,1", 70, 14);
writer.insertToDocument(list);
// writer.saveAsHtml("E://test.html");
writer.saveAs("E://TestDocBenjamin.doc");
// writer.close();
}
}
cob操作word
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang.SystemUtils;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
/**
*
* 類: <code> WordUtils </code>
* 功能描述:
* 建立人: 王星樂
* 建立日期: 2013-8-2 上午10:02:23
* 開發環境: JDK6.0
*/
public class WordUtils {
/**
*
* 功能描述: 將word轉換為pdf,注意此方法不會關閉is和os
* @param is 輸入流
* @param os 輸出流
*/
public static void convertToPdf(InputStream is, OutputStream os) {
ActionCallBack action = new ActionCallBack() {
@Override
public void execute(String srcFilePath, String destFilePath) {
int wdFormatPDF = 17;// PDF 格式
ActiveXComponent app = null;
Dispatch doc = null;
try {
app = new ActiveXComponent
("Word.Application");
app.setProperty("Visible", new Variant
(false));
Dispatch docs = app.getProperty
("Documents").toDispatch();
doc = Dispatch.call(docs, "Open",
srcFilePath).toDispatch();
Dispatch.call(doc, "SaveAs", destFilePath,
wdFormatPDF);
Dispatch.call(doc, "Close", false);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
if (app != null) {
app.invoke("Quit", new Variant[]
{});
}
ComThread.Release();
}
}
};
process(is, os, ".doc", ".pdf", action);
}
/**
*
* 功能描述: 將word轉換為html,注意此方法不會關閉is和os
* @param is 輸入流
* @param os 輸出流
*/
public static void convertToHtml(InputStream is, String path) {
//臨時檔案目錄
File tempDir = new File(SystemUtils.getJavaIoTmpDir().getAbsolutePath
(), "wordTemp");
if (!tempDir.exists()) {
tempDir.mkdir();
}
File srcFile = new File(tempDir, UUID.randomUUID().toString() + ".doc");
if (srcFile.exists()) {
srcFile.delete();
}
File destFile = new File(tempDir, path);
if (destFile.exists()) {
destFile.delete();
}
//從輸入流生成src檔案
OutputStream srcOs = null;
try {
srcOs = new BufferedOutputStream(new FileOutputStream
(srcFile));
copy(is, srcOs);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
close(srcOs);
}
int wdFormatHTML = 8;// HTML 格式
ActiveXComponent app = null;
Dispatch doc = null;
try {
app = new ActiveXComponent("Word.Application");
app.setProperty("Visible", new Variant(false));
Dispatch docs = app.getProperty("Documents").toDispatch();
doc = Dispatch.call(docs, "Open", srcFile.getAbsolutePath
()).toDispatch();
Dispatch.call(doc, "SaveAs", path, wdFormatHTML);
Dispatch.call(doc, "Close", false);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
if (app != null) {
app.invoke("Quit", new Variant[] {});
}
ComThread.Release();
}
}
/**
*
* 功能描述: 替換書籤,注意此方法不會關閉is和os
* @param is 輸入流
* @param os 輸出流
* @param bookmarkList 書籤集合
* @param dataList 替換內容集合
*/
public static void replaceBookmarks(InputStream is, OutputStream os, final
List<String> bookmarkList,
final List<String> dataList) {
ActionCallBack action = new ActionCallBack() {
@Override
public void execute(String srcFilePath, String destFilePath) {
ActiveXComponent app = null;
Dispatch doc = null;
try {
app = new ActiveXComponent
("Word.Application");
app.setProperty("Visible", new Variant
(false));
Dispatch documents = app.getProperty
("Documents").toDispatch();
doc = Dispatch.call(documents, "Open",
srcFilePath).toDispatch();
Dispatch activeDocument =
app.getProperty("ActiveDocument").toDispatch();
Dispatch bookMarks = Dispatch.call
(activeDocument, "Bookmarks").toDispatch();
//替換書籤
for (int i = 0; i < bookmarkList.size(); i++)
{
boolean bookMarkExist =
Dispatch.call(bookMarks, "Exists", bookmarkList.get(i)).changeType(
Variant.VariantBoolean).getBoolean();
if (bookMarkExist == true) {
Dispatch rangeItem =
Dispatch.call(bookMarks, "Item", bookmarkList.get(i)).toDispatch();
Dispatch range =
Dispatch.call(rangeItem, "Range").toDispatch();
Dispatch.put(range,
"Text", new Variant(dataList.get(i)));
}
}
Dispatch.call(doc, "SaveAs", destFilePath);
Dispatch.call(doc, "Close", false);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
if (app != null) {
app.invoke("Quit", new Variant[]
{});
}
ComThread.Release();
}
}
};
process(is, os, ".doc", ".doc", action);
}
/**
*
* 功能描述: 模板方法,做初始化工作,呼叫回撥函式,執行清理工作。
* @param is
* @param os
* @param srcFileExtension
* @param destFileExtension
* @param action
*/
public static void process(InputStream is, OutputStream os, String srcFileExtension,
String destFileExtension,
ActionCallBack action) {
//臨時檔案目錄
File tempDir = new File(SystemUtils.getJavaIoTmpDir().getAbsolutePath
(), "wordTemp");
if (!tempDir.exists()) {
tempDir.mkdir();
}
File srcFile = new File(tempDir, UUID.randomUUID().toString() +
srcFileExtension);
if (srcFile.exists()) {
srcFile.delete();
}
File destFile = new File(tempDir, UUID.randomUUID().toString() +
destFileExtension);
if (destFile.exists()) {
destFile.delete();
}
//從輸入流生成src檔案
OutputStream srcOs = null;
try {
srcOs = new BufferedOutputStream(new FileOutputStream
(srcFile));
copy(is, srcOs);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
close(srcOs);
}
action.execute(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
//拷貝dest檔案到輸出流
InputStream destIs = null;
try {
destIs = new BufferedInputStream(new FileInputStream
(destFile));
copy(destIs, os);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
close(destIs);
}
//刪除臨時檔案
if (srcFile.exists()) {
srcFile.delete();
}
if (destFile.exists()) {
destFile.delete();
}
}
private interface ActionCallBack {
void execute(String srcFilePath, String destFilePath);
}
/**
*
* 功能描述: 將輸入流的資料拷貝到輸出流
* @param is
* @param os
* @throws IOException
*/
private static void copy(InputStream is, OutputStream os) throws IOException {
int len = 0;
byte[] buffer = new byte[1024 * 4];
while (-1 != (len = is.read(buffer))) {
os.write(buffer, 0, len);
}
}
/**
*
* 功能描述: 關閉流
* @param s
*/
private static void close(Closeable s) {
if (s != null) {
try {
s.close();
} catch (IOException e) {
//忽略
}
}
}
}
//0:Microsoft Word 97 - 2003 文件 (.doc)
//1:Microsoft Word 97 - 2003 模板 (.dot)
//2:文字文件 (.txt)
//3:文字文件 (.txt)
//4:文字文件 (.txt)
//5:文字文件 (.txt)
//6:RTF 格式 (.rtf)
//7:文字文件 (.txt)
//8:HTML 文件 (.htm)(帶資料夾)
//9:MHTML 文件 (.mht)(單檔案)
//10:MHTML 文件 (.mht)(單檔案)
//11:XML 文件 (.xml)
//12:Microsoft Word 文件 (.docx)
//13:Microsoft Word 啟用巨集的文件 (.docm)
//14:Microsoft Word 模板 (.dotx)
//15:Microsoft Word 啟用巨集的模板 (.dotm)
//16:Microsoft Word 文件 (.docx)
//17:PDF 檔案 (.pdf)
//18:XPS 文件 (.xps)
//19:XML 文件 (.xml)
//20:XML 文件 (.xml)
//21:XML 文件 (.xml)
//22:XML 文件 (.xml)
//23:OpenDocument 文字 (.odt)
//24:WTF 檔案 (.wtf)
用Java操作Office 2007
Office 2007平臺作為一個構建富客戶端應用程式的基本平臺,並通過不同的手段使用Java來進行互操作。 但是,有一個Office/Java互操作的方面沒有考慮到,那就是使Office和Java共同工作,也就是說讓Java應用程式來操作Office文件:比如建立文件,編輯文件,收集資料等等。
從以往看來,這其中經常會出現一些問題,這是由於Office文件(主要是Word,Excel和PowerPoint)是儲存在一個二進位制格式檔案中,在COM中被稱為結構化儲存格式, 是一個通過COM介面的層次化二進位制格式。 對COM開發者(或者其他使用COM相關語言的開發者,如Visual Basic, Delphi 和C++/ATL)而言非常方便,但產生的檔案對於那些不能“講COM”的語言是無法訪問的。有許許多多的應用程式都是為了讓Java語言可以訪問這些檔案的內容;比如大家都知道Excel可以讀取逗號分隔符檔案(CSV),因此,Java應用程式相應將資料匯出到Excel友好的格式時一般會選用CSV格式(或是其他醜陋的格式)。Word則是可以讀取富文字格式(RTF)檔案,而RTF標準是公開和有詳細文件的。Office的後來者,Office 2003,引入了一個新的XML格式(WordML),Java開發者可以用它來讀寫Office文件,但是這些格式並沒有很好的文件,Java開發者頻繁的發現自己是通過試錯法來進行WordML格式的學習。 各種各樣的開源專案都參與進來想要解決這個問題,比如Apache的POI框架,可以用來讀寫Excel文件,還有各種各樣的Java-COM解決方案,這些解決方案一般傾向於使用和Office自己使用的結構化儲存應用程式介面相同的應用程式介面進行Excel文件的讀寫,但很難滿足需要,直到現在,開發者不得不指出Office文件格式的內部結構是一個非常複雜的結構,另外一點毋庸置疑的是它是一個沒有完整文件的結構。
總體上來說,如果溫和一點說的話,Java/Office的故事是一個非常討厭的境況。對於Java的開發人員而言,他們要麼一邊嘴裡說著“Office這種破東西怎麼還會有人想去用它”一邊用記憶裡的伊索寓言來安慰自己,要麼乾脆告訴那些使用Office的客戶由於Microsoft和Sun兩家公司之間的訴訟,Java不能操作Office。
對於Office 2007來說,微軟毫無疑問的邁出瞭解決這些問題的一大步。沒有比原始的JDK更復雜的東西---也就是說並不要求使用一些第三方的庫---Java應用程式現在可以讀寫任何Office 2007的文件,這是由於Office 2007文件現在使用的是XML文件的ZIP格式檔案。 這種格式被稱作“OpenMXL”規範並且已經被提交到歐洲計算機制造商協會(ECMA),這個協會同樣擁有C#語言和CLI執行時規範,所有的OpenXML規範現在都可以被任何人自由的從ECMA的網站下載。 除了這些,再安裝好Office 2007(為了驗證和作一些測試)和一個標準的Java6 JDK安裝,Java現在可以開啟任何的Office 2007文件,找出來文件中間的內容,操作它們,並且再次儲存這些資料。
與上篇文章不同,在這篇文章中,除了建立一個簡單的應用程式之外,程式碼將會使用一種首先由Stuart Halloway提出的、被稱作探索測試(exploration testing)的技術。在一個探索測試中,開發者編寫單元測試用來探索應用程式介面,使用單元測試世界中的斷言驗證結果的正確性。探索測試帶來的好處是當一個新版本的應用程式介面可用時---在這個例子中,可能是一個新版本的Office---執行這些測試可以用來確認新版本的採用不會影響到原本對應用程式介面的使用。
對於初學者來說,讓我們首先快速的瞭解一下Office 2007文件。首先看一個僅僅包含文字的Word 2007文件,就像下面一樣:
當儲存的時候,使用Word 2007將它儲存為“Hello.docx”,除非你使用了向後相容格式,比如說Office 2003的WordML格式,或者是更老的Word 97二進位制結構化儲存格式。“.docx”檔案是OpenXML格式的,微軟的文件中聲稱該格式是XML文件的ZIP壓縮格式檔案,這些檔案中包含了文件中的資料和格式,儲存的方式與之前的Office版本中的二進位制結構化儲存應用程式介面儲存資料的方式有些類似。如果這是真的,那麼使用Java中提供的用來處理ZIP和TAR格式的“jar”實用工具應該可以展示這些內容,而事實上,它的確可以:
Word 2007文件的基本格式已經非常明顯了,僅僅通過控制檯的輸出就可以看到。(事實上,“jar”實用工具所展示的這激動人心的一切,說明java.util.jar和/或 java.util.zip包同樣可以簡單的訪問這些內容。)幾乎沒有對規範作任何的破解,很明顯,文件中的主要內容應該被儲存到了“document.xml”檔案中,剩餘的其他XML檔案則應該是各種各樣的輔助部分,比如文件中應用到的字型(fontTable.xml)和使用到的Office主題(theme/theme1.xml),等等。
是時間來編寫一些探索測試了。(我們鼓勵感興趣的讀者開啟一個文字編輯器或者整合開發環境,並將下面的內容填入你的JUnit 4測試類當中,並且擴充套件這些測試。) 使用JUnit 4,第一個測試是為了簡單的確認檔案在我們預想的位置(顯然這是下面測試可以執行的一個必要的需求)。
@Test public void verifyFileIsThere() { assertTrue(new File("hello.docx").exists()); assertTrue(new File("hello.docx").canRead()); assertTrue(new File("hello.docx").canWrite()); }
下面的測試簡單的驗證了我們可以使用Java庫中的java.util.zip.ZipFile來開啟這個檔案:
@Test public void openFile() throws IOException, ZipException { ZipFile docxFile = new ZipFile(new File("hello.docx")); assertEquals(docxFile.getName(), "hello.docx"); }
現在一切看來都非常不錯。Java的ZipFile類正確的識別了我們的檔案,一個zip檔案,如果我們還能繼續保持這樣的運氣,讓我們繼續我們的測試,來遍歷一下,識別文件中的內容並找出其中的資料。讓我們編寫一個快速的測試來從“document.xml”檔案中找出所有的內容。
@Test public void listContents() throws IOException, ZipException { boolean documentFound = false; ZipFile docxFile = new ZipFile(new File("hello.docx")); Enumeration entriesIter = docxFile.entries(); while (entriesIter.hasMoreElements()) { ZipEntry entry = entriesIter.nextElement(); if (entry.getName().equals("document.xml")) documentFound = true; } assertTrue(documentFound); }
令人詫異的是,當我們執行測試的時候,測試過程產生了一個失敗;並沒有找到“document.xml”檔案,這是由於ZipFile/ZipEntry 應用程式介面需要壓縮檔案中完整的路徑名稱。將測試中的路徑改為“word/document.xml”,測試就通過了。
很好,我們已經找到檔案了,下面讓我們開啟這個檔案看看XML裡面是什麼。這非常簡單,因為ZipFile有一個返回ZipEntry的應用程式介面。
@Test public void getDocument() throws IOException, ZipException { ZipFile docxFile =