如何在Java中打开和操作Word文档/模板? [英] How to open and manipulate Word document/template in Java?

查看:93
本文介绍了如何在Java中打开和操作Word文档/模板?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要打开一个 .doc / .dot / .docx / .dotx (我不挑剔,我只是想让它工作)文件,
为占位符(或类似的东西)解析它,
放置我自己的数据,
然后返回生成的 .doc / .docx / .dotx / .pdf document。



除此之外,我还需要一些工具来实现这一目标。



我一直在寻找适合我需求的东西,但我找不到任何东西。
Docmosis,Javadocx,Aspose等工具是商业用途。
从我读过的内容来看,Apache POI远未成功实现这一点(他们目前没有任何官方开发人员在框架的Word部分工作)。



<看来可以解决的唯一问题是OpenOffice UNO API。
但对于那些从未使用过这个API的人来说,这是一个相当大的字节(像我一样)。



所以,如果我要跳到这个,我我需要确保自己走在正确的道路上。



有人可以给我一些建议吗?

解决方案

我知道自从我发布这个问题已经有很长一段时间了,我说我会在完成后发布我的解决方案。
所以这就是。



我希望有一天能帮到某人。
这是一个完整的工作类,您只需将它放在您的应用程序中,并将TEMPLATE_DIRECTORY_ROOT目录与.docx模板放在根目录中。



用法非常简单。
您将占位符(键)放在.docx文件中,然后传递文件名和包含该文件的相应键值对的Map。



享受!

  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;

公共类DocxManipulator {

private static final String MAIN_DOCUMENT_PATH =word / document.xml;
private static final String TEMPLATE_DIRECTORY_ROOT =TEMPLATES_DIRECTORY /;


/ * PUBLIC METHODS * /

/ **
*从给定模板生成.docx文件和替换数据
*
* @param templateName
*模板数据
* @param substitutionData
*带有一组键值对的哈希映射,代表
*替换数据
* @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 {

//解压缩.docx文件
unzip(新文件(templateLocation),新文件(userTempDir));

//更改数据
changeData(新文件(userTempDir + MAIN_DOCUMENT_PATH),substitutionData);

//重新压缩.docx文件
zip(新文件(userTempDir),新文件(userTempDir + templateName));

//发送HTTP响应
sendDOCXResponse(新文件(userTempDir + templateName),templateName);

//清理临时数据
deleteTempData(new File(userTempDir));
}
catch(IOException ioe){
System.out.println(ioe.getMessage());
返回false;
}

返回true;
}


/ *私有方法* /

/ **
* Unzipps指定的ZIP文件到指定目录
*
* @param zipfile
*源ZIP文件
* @param目录
*目的地目录
* @throws IOException
* /
private static void unzip(File zipfile,File directory)throws IOException {

ZipFile zfile = new ZipFile(zipfile);
枚举<?扩展ZipEntry> entries = zfile.entries();

while(entries.hasMoreElements()){
ZipEntry entry = entries.nextElement();
文件file = new File(directory,entry.getName());
if(entry.isDirectory()){
file.mkdirs();
}
else {
file.getParentFile()。mkdirs();
InputStream in = zfile.getInputStream(entry);
try {
copy(in,file);
}
最后{
in.close();
}
}
}
}


/ **
*替换目标文件中的密钥及相应的数据
*
* @param targetFile
*目标文件
* @param substitutionData
*数据键值对的映射
* @throws IOException
* /
@SuppressWarnings({unchecked,rawtypes})
private static void changeData(File targetFile,Map< String,String> substitutionData)抛出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();
扔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();
扔e;
}
}

/ **
* Zipps指定的目录及其所有子目录
*
* @param目录
*指定目录
* @param zipfile
*输出ZIP文件名
* @throws IOException
* /
private static void zip(文件目录,文件zipfile)抛出IOException {

URI base = directory.toURI();
Deque< File> queue = new LinkedList< File>();
queue.push(目录);
OutputStream out = new FileOutputStream(zipfile);
关闭res = out;

试试{
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();
}
}

/ **
*向客户发送包含.docx文件的HTTP响应
*
* @param generatedFile
*生成的.docx文件的路径
* @param fileName
*向用户显示的生成文件的文件名
* @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();

//通知JSF不要继续其余的生命周期
facesContext.responseComplete();
}


/ **
*删除目录及其所有子目录
*
* @param file
*指定directory
* @throws IOException
* /
public static void deleteTempData(File file)throws IOException {

if(file.isDirectory()){

//目录为空,然后将其删除
if(file.list()。length == 0)
file.delete();
else {
//列出所有目录内容
String files [] = file.list();

for(String temp:files){
//构造文件结构
文件fileDelete = new File(file,temp);
//递归删除
deleteTempData(fileDelete);
}

//再次检查目录,如果为空则删除它
if(file.list()。length == 0)
file.delete( );
}
} else {
//如果是文件,则删除它
file.delete();
}
}

private static void copy(InputStream in,OutputStream out)抛出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(文件文件,OutputStream out)抛出IOException {
InputStream in = new FileInputStream(file);
试试{
copy(in,out);
}最后{
in.close();
}
}

private static void copy(InputStream in,File file)抛出IOException {
OutputStream out = new FileOutputStream(file);
试试{
copy(in,out);
}最后{
out.close();
}
}

}


I need to open a .doc/.dot/.docx/.dotx (I'm not picky, I just want it to work) document, parse it for placeholders (or something similar), put my own data, and then return generated .doc/.docx/.dotx/.pdf document.

And on top of all that, I need the tools to accomplish that to be free.

I've searched around for something that would suit my needs, but I can't find anything. Tools like Docmosis, Javadocx, Aspose etc. are commercial. From what I've read, Apache POI is nowhere near successfully implementing this (they currently don't have any official developer working on Word part of framework).

The only thing that looks that could do the trick is OpenOffice UNO API. But that is a pretty big byte for someone that has never used this API (like me).

So if I am going to jump into this, I need to make sure that I am on the right path.

Can someone give me some advice on this?

解决方案

I know it's been a long time since I've posted this question, and I said that I would post my solution when I'm finished. So here it is.

I hope that it will help someone someday. This is a full working class, and all you have to do is put it in your application, and place TEMPLATE_DIRECTORY_ROOT directory with .docx templates in your root directory.

Usage is very simple. You put placeholders (key) in your .docx file, and then pass file name and Map containing corresponding key-value pairs for that file.

Enjoy!

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();
        }
     }

}

这篇关于如何在Java中打开和操作Word文档/模板?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆