在 Windows 平台下用 Java 通过模板创建 Word 文档的简单方法






4.75/5 (4投票s)
使用 JNI 和 COM 技术在 Windows 平台下的 Java 中生成 Word 文档
引言
有一些开源项目可以在 Java 中创建 Word 文档,比如 Apache poi,itext 等。 但是,在 Windows 平台下,使用 COM 是在 Java 中创建 Word 文档的一种简单方法。 因此,我将向您展示如何使用 JNI 技术来调用 Windows COM。
设置开发环境
- 从 http://www.eclipse.org 下载最新版本的 Eclipse,并将压缩文件解压到 C:\Eclipse。
- 从 http://www.codeblocks.org 下载最新版本的
Code::Blocks
,并将其安装到 C:\CodeBlocks。 - 从 http://sourceforge.net/projects/vole 下载最新版本的 vole,它是一个简洁的 C++ COM/自动化驱动程序,我们可以使用它轻松创建 Word 文档。 而且 vole 依赖于 STLSoft 库,因此我们也需要下载 STLSoft - http://stlsoft.org。
- 将文件 vole-0.7.2.zip 解压到 C:\vole-0.7.2,并将目录 “vole” 从 C:\vole-0.7.2\include 复制到 C:\CodeBlocks\MinGW\include。
- 将文件 stlsoft-1.9.100-hdrs.zip 解压到 C:\stlsoft-1.9.100-hdrs,并将所有目录从 C:\stlsoft-1.9.100-hdrs\include 复制到 C:\CodeBlocks\MinGW\include。
程序开发
- 在 Eclipse 中创建一个新的 Java 项目
zzutils
,并创建一个名为Utils
的新类,包名为org.zerozone.util
。我们将定义一个名为generateWordDocument
的 native 方法,该方法包含 5 个参数。 方法定义如下所示public static native void generateWordDocument(String fileName, String[] bookmarkNames, int bookmarkCount, String[] values, int valueCount);
参数描述
fileName
,保存处理结果的文件bookmarkNames
,一个包含要插入的书签的数组bookmarkCount
,一个数字,指示将处理多少个书签values
,一个包含将插入到正确书签中的值的数组valueCount
,一个数字,指示将插入多少个值
- 使用
javah
生成 C++ 头文件,javah
是 jdk 中的一个工具。 创建一个名为 generate_header.bat 的批处理文件,其内容如下所示"C:\Program Files\Java\jdk1.6.0_10\bin\javah" -classpath .;./bin org.zerozone.util.Utils
运行此 bat 文件,然后我们可以在 zztuils 项目目录下获得一个名为 org_zerozone_util_Utils.h 的 C++ 头文件。
- 在 Code::Blocks 中创建一个名为
zzutils
的新动态库项目。
首先,将头文件 org_zerozone_util_Utils.h 复制到项目目录,并将此头文件添加到项目中,然后创建一个名为 utils.cpp 的新 CPP 文件,其内容如下所示#include <windows.h> #include "org_zerozone_utils_Utils.h" #include "cn_net_ynst_kjjl_util_Utils.h" #include <vole/vole.hpp> #include <comstl/util/initialisers.hpp> using vole::object; using vole::collection; char* jstringToWindows(JNIEnv *env, jstring jstr) { int length = env->GetStringLength(jstr); const jchar* jcstr = env->GetStringChars(jstr, 0); char* rtn = (char*)malloc(length*2+1); int size = 0; size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL); if(size <= 0) { free(rtn); rtn = NULL; return NULL; } env->ReleaseStringChars(jstr, jcstr); rtn[size] = 0; return rtn; } jstring windowsToJstring(JNIEnv* env, char* str) { jstring rtn = 0; int slen = strlen(str); unsigned short* buffer = 0; if(slen == 0) { rtn = env->NewStringUTF(str); } else { int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, NULL, 0); buffer = (unsigned short*)malloc(length*2 + 1); if( MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) > 0) rtn = env->NewString((jchar*)buffer, length); } if(buffer) { free(buffer); buffer = NULL; } return rtn; } } JNIEXPORT void JNICALL Java_cn_net_ynst_kjjl_util_Utils_generateWordDocument (JNIEnv *env, jclass clazz, jstring fileName, jobjectArray bookmarkNames, jint bookmarkCount, jobjectArray values, jint valueCount) { if(bookmarkCount != valueCount) return; int len = 0; char* _fileName = jstringToWindows(env, fileName); len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_fileName, strlen(_fileName) + 1, NULL, 0); wchar_t* _w_fileName = new wchar_t[len]; MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_fileName, -1, _w_fileName, len); comstl::com_init init; object word = object::create("Word.Application", CLSCTX_LOCAL_SERVER, vole::coercion_level::naturalPromotion); word.put_property(L"Visible", false); collection documents = word.get_property<collection>(L"Documents"); object document = documents.invoke_method<object> (L"Open", (LPCWSTR)_w_fileName); collection bookmarks = document.get_property<collection>(L"Bookmarks"); int count = bookmarks.get_property<int>(L"Count"); for(int i = 1; i <= count; i++) { object bookmark = bookmarks.invoke_method<object>(L"Item", i); std::string name = bookmark.get_property<std::string>(L"Name"); for(int j = 0; j < bookmarkCount; j++) { jstring bookmarkName = (jstring)env->GetObjectArrayElement(bookmarkNames, j); const char* _bookmarkName = env->GetStringUTFChars(bookmarkName, 0); if(strcmp(name.c_str(), _bookmarkName) == 0) { jstring value = (jstring)env->GetObjectArrayElement(values, j); char* _value = jstringToWindows(env, value); len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_value, strlen(_value) + 1, NULL, 0); wchar_t* _w_value = new wchar_t[len]; MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_value, -1, _w_value, len); object range = bookmark.get_property<object>(L"Range"); range.invoke_method_v(L"InsertAfter", (LPCWSTR)_w_value); free(_value); free(_w_value); _value = NULL; _w_value = NULL; env->ReleaseStringUTFChars(bookmarkName, _bookmarkName); break; } env->ReleaseStringUTFChars(bookmarkName, _bookmarkName); } } document.invoke_method_v(L"Save"); document.invoke_method_v(L"Close", false); word.invoke_method_v(L"Quit"); free(_fileName); free(_w_fileName); }
- 在 Word 中创建一个新的 Word 文档,并插入两个名为
title
和year
的书签,保存并将其命名为 1.doc,路径为 C:\。 我们将使用它作为模板文件。
完成上述所有步骤后,我们可以在 Code::Blocks
中对其进行编译,并获得一个名为 zztuils.dll 的文件,请将此文件复制到由 eclipse 创建的项目 zzutils
中。 然后我们可以在类 Utils
中运行 main
方法。
优点 & 缺点
优点
- 我们可以使用 COM 技术的特性轻松创建 Word 文档。
- 我们可以编写比使用 apache poi 或其他项目的程序更少的代码。
缺点
- 该程序只能在 Windows 平台下运行。
- 将其部署到 tomcat 中,调用 5-6 次后可能会崩溃。 我将尝试使用 web service 的方式来避免这个问题。
历史
- 2010 年 12 月 2 日:首次发布