如何在Java中打开和操作Word文档/模板?
我需要打开一个如何在Java中打开和操作Word文档/模板?,java,ms-word,Java,Ms Word,我需要打开一个.doc/.dot/.docx/.dotx(我不挑剔,我只是想让它工作)文档, 分析它的占位符(或类似内容), 把我自己的数据, 然后返回生成的.doc/.docx/.dotx/.pdf文档 最重要的是,我需要免费的工具来实现这一点 我到处寻找适合我需要的东西,但什么也找不到。 Docmosis、Javadocx、Aspose等工具都是商用的。 据我所知,ApachePOI远未成功实现这一点(他们目前没有任何官方开发人员参与框架的Word部分) 看起来唯一能做到这一点的是OpenO
.doc/.dot/.docx/.dotx
(我不挑剔,我只是想让它工作)文档,
分析它的占位符(或类似内容),
把我自己的数据,
然后返回生成的.doc/.docx/.dotx/.pdf
文档
最重要的是,我需要免费的工具来实现这一点
我到处寻找适合我需要的东西,但什么也找不到。
Docmosis、Javadocx、Aspose等工具都是商用的。
据我所知,ApachePOI远未成功实现这一点(他们目前没有任何官方开发人员参与框架的Word部分)
看起来唯一能做到这一点的是OpenOffice UNO API。
但对于从未使用过这个API的人(比如我),这是一个相当大的字节
因此,如果我要跳进这一步,我需要确保我走的是正确的道路
有人能给我一些建议吗?我的处境和你差不多,我不得不一次修改一大堆MS Word合并模板。在谷歌搜索了很多东西试图找到一个Java解决方案之后,我终于安装了Visual Studio 2010 Express,它是免费的,并且用C#完成了这项工作。因为docx文件只是xml文件的zip存档(加上用于嵌入对象(如图像)的任何二进制文件),我们通过解压zip文件满足了这一要求,将document.xml提供给模板引擎(我们使用的),模板引擎为我们进行合并,然后压缩输出文档以获得新的docx文件 然后,模板文档就是一个普通的docx,带有嵌入的freemarker表达式/指令,可以在Word中编辑 由于(取消)压缩可以用JDK完成,而且Freemarker是开源的,所以您不需要支付任何许可费,甚至word本身也不需要
限制是这种方法只能发出docx或rtf文件,并且输出文档将具有与模板相同的文件类型。如果您需要将文档转换为另一种格式(如pdf),则必须单独解决该问题。我知道我已经很久没有发布此问题了,我说过我将在完成后发布我的解决方案。 就是这样 我希望有一天它会帮助别人。 这是一个完整的工作类,您只需将其放入应用程序中,并将TEMPLATE\u DIRECTORY\u根目录与.docx templates一起放入根目录 用法很简单。 在.docx文件中放置占位符(键),然后传递包含该文件对应键值对的文件名和映射 享受吧
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
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.InputStreamReader;
import java.io.OutputStream;
import java.net.URI;
import java.util.Deque;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;
public class DocxManipulator {
private static final String MAIN_DOCUMENT_PATH = "word/document.xml";
private static final String TEMPLATE_DIRECTORY_ROOT = "TEMPLATES_DIRECTORY/";
/* PUBLIC METHODS */
/**
* Generates .docx document from given template and the substitution data
*
* @param templateName
* Template data
* @param substitutionData
* Hash map with the set of key-value pairs that represent
* substitution data
* @return
*/
public static Boolean generateAndSendDocx(String templateName, Map<String,String> substitutionData) {
String templateLocation = TEMPLATE_DIRECTORY_ROOT + templateName;
String userTempDir = UUID.randomUUID().toString();
userTempDir = TEMPLATE_DIRECTORY_ROOT + userTempDir + "/";
try {
// Unzip .docx file
unzip(new File(templateLocation), new File(userTempDir));
// Change data
changeData(new File(userTempDir + MAIN_DOCUMENT_PATH), substitutionData);
// Rezip .docx file
zip(new File(userTempDir), new File(userTempDir + templateName));
// Send HTTP response
sendDOCXResponse(new File(userTempDir + templateName), templateName);
// Clean temp data
deleteTempData(new File(userTempDir));
}
catch (IOException ioe) {
System.out.println(ioe.getMessage());
return false;
}
return true;
}
/* PRIVATE METHODS */
/**
* Unzipps specified ZIP file to specified directory
*
* @param zipfile
* Source ZIP file
* @param directory
* Destination directory
* @throws IOException
*/
private static void unzip(File zipfile, File directory) throws IOException {
ZipFile zfile = new ZipFile(zipfile);
Enumeration<? extends ZipEntry> entries = zfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File file = new File(directory, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
}
else {
file.getParentFile().mkdirs();
InputStream in = zfile.getInputStream(entry);
try {
copy(in, file);
}
finally {
in.close();
}
}
}
}
/**
* Substitutes keys found in target file with corresponding data
*
* @param targetFile
* Target file
* @param substitutionData
* Map of key-value pairs of data
* @throws IOException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static void changeData(File targetFile, Map<String,String> substitutionData) throws IOException{
BufferedReader br = null;
String docxTemplate = "";
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(targetFile), "UTF-8"));
String temp;
while( (temp = br.readLine()) != null)
docxTemplate = docxTemplate + temp;
br.close();
targetFile.delete();
}
catch (IOException e) {
br.close();
throw e;
}
Iterator substitutionDataIterator = substitutionData.entrySet().iterator();
while(substitutionDataIterator.hasNext()){
Map.Entry<String,String> pair = (Map.Entry<String,String>)substitutionDataIterator.next();
if(docxTemplate.contains(pair.getKey())){
if(pair.getValue() != null)
docxTemplate = docxTemplate.replace(pair.getKey(), pair.getValue());
else
docxTemplate = docxTemplate.replace(pair.getKey(), "NEDOSTAJE");
}
}
FileOutputStream fos = null;
try{
fos = new FileOutputStream(targetFile);
fos.write(docxTemplate.getBytes("UTF-8"));
fos.close();
}
catch (IOException e) {
fos.close();
throw e;
}
}
/**
* Zipps specified directory and all its subdirectories
*
* @param directory
* Specified directory
* @param zipfile
* Output ZIP file name
* @throws IOException
*/
private static void zip(File directory, File zipfile) throws IOException {
URI base = directory.toURI();
Deque<File> queue = new LinkedList<File>();
queue.push(directory);
OutputStream out = new FileOutputStream(zipfile);
Closeable res = out;
try {
ZipOutputStream zout = new ZipOutputStream(out);
res = zout;
while (!queue.isEmpty()) {
directory = queue.pop();
for (File kid : directory.listFiles()) {
String name = base.relativize(kid.toURI()).getPath();
if (kid.isDirectory()) {
queue.push(kid);
name = name.endsWith("/") ? name : name + "/";
zout.putNextEntry(new ZipEntry(name));
}
else {
if(kid.getName().contains(".docx"))
continue;
zout.putNextEntry(new ZipEntry(name));
copy(kid, zout);
zout.closeEntry();
}
}
}
}
finally {
res.close();
}
}
/**
* Sends HTTP Response containing .docx file to Client
*
* @param generatedFile
* Path to generated .docx file
* @param fileName
* File name of generated file that is being presented to user
* @throws IOException
*/
private static void sendDOCXResponse(File generatedFile, String fileName) throws IOException {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletResponse response = (HttpServletResponse) externalContext
.getResponse();
BufferedInputStream input = null;
BufferedOutputStream output = null;
response.reset();
response.setHeader("Content-Type", "application/msword");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
response.setHeader("Content-Length",String.valueOf(generatedFile.length()));
input = new BufferedInputStream(new FileInputStream(generatedFile), 10240);
output = new BufferedOutputStream(response.getOutputStream(), 10240);
byte[] buffer = new byte[10240];
for (int length; (length = input.read(buffer)) > 0;) {
output.write(buffer, 0, length);
}
output.flush();
input.close();
output.close();
// Inform JSF not to proceed with rest of life cycle
facesContext.responseComplete();
}
/**
* Deletes directory and all its subdirectories
*
* @param file
* Specified directory
* @throws IOException
*/
public static void deleteTempData(File file) throws IOException {
if (file.isDirectory()) {
// directory is empty, then delete it
if (file.list().length == 0)
file.delete();
else {
// list all the directory contents
String files[] = file.list();
for (String temp : files) {
// construct the file structure
File fileDelete = new File(file, temp);
// recursive delete
deleteTempData(fileDelete);
}
// check the directory again, if empty then delete it
if (file.list().length == 0)
file.delete();
}
} else {
// if file, then delete it
file.delete();
}
}
private static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
while (true) {
int readCount = in.read(buffer);
if (readCount < 0) {
break;
}
out.write(buffer, 0, readCount);
}
}
private static void copy(File file, OutputStream out) throws IOException {
InputStream in = new FileInputStream(file);
try {
copy(in, out);
} finally {
in.close();
}
}
private static void copy(InputStream in, File file) throws IOException {
OutputStream out = new FileOutputStream(file);
try {
copy(in, out);
} finally {
out.close();
}
}
}
import java.io.BufferedInputStream;
导入java.io.BufferedOutputStream;
导入java.io.BufferedReader;
导入java.io.Closeable;
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.FileOutputStream;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.InputStreamReader;
导入java.io.OutputStream;
导入java.net.URI;
导入java.util.Deque;
导入java.util.Enumeration;
导入java.util.HashMap;
导入java.util.Iterator;
导入java.util.LinkedList;
导入java.util.Map;
导入java.util.UUID;
导入java.util.zip.ZipEntry;
导入java.util.zip.ZipFile;
导入java.util.zip.ZipoutStream;
导入javax.faces.context.ExternalContext;
导入javax.faces.context.FacesContext;
导入javax.servlet.http.HttpServletResponse;
公共类docx操作器{
私有静态最终字符串MAIN\u DOCUMENT\u PATH=“word/DOCUMENT.xml”;
私有静态最终字符串TEMPLATE_DIRECTORY_ROOT=“TEMPLATES_DIRECTORY/”;
/*公共方法*/
/**
*根据给定模板和替换数据生成.docx文档
*
*@param templateName
*模板数据
*@param替换数据
*哈希映射,其中包含一组表示
*替代数据
*@返回
*/
公共静态布尔generateAndSendDocx(字符串模板名,映射替换数据){
字符串templateLocation=TEMPLATE\u DIRECTORY\u ROOT+templateName;
字符串userTempDir=UUID.randomUUID().toString();
userTempDir=TEMPLATE_DIRECTORY_ROOT+userTempDir+“/”;
试一试{
//解压.docx文件
解压(新文件(templateLocation)、新文件(userTempDir));
//更改数据
changeData(新文件(userTempDir+主文档路径)、替换数据);
//docx文件
zip(新文件(userTempDir)、新文件(userTempDir+templateName));
//发送HTTP响应
sendDOCXResponse(新文件(userTempDir+templateName)、templateName);
//清除温度数据
删除tempdata(新文件(userTempDir));
}
捕获(ioe异常ioe){
System.out.println(ioe.getMessage());
返回false;
}
返回true;
}
/*私有方法*/
/**
*将指定的ZIP文件解压缩到指定目录
*
*@param-zipfile
*源压缩文件
*@param目录
*目标目录
*@抛出异常
*/
私有静态void解压(文件zipfile,文件目录)引发IOException{
ZipFile zfile=新ZipFile(ZipFile);
枚举我最终依赖于ApachePOI3.12和处理段落(也从表、页眉/页脚和脚注中分别提取段落,因为这样的段落不会被返回)
处理代码()和单元测试是。我最近处理过类似的问题:
接受模板“.docx”文件的工具,通过计算传递的参数上下文来处理该文件,并输出“.docx”文件作为处理结果
最后,上帝给我们带来了:)。
此产品的主要功能是:
1.groovy代码注入作为模板文件中的脚本(参数注入,e