【问题标题】:How to edit MS Word documents using Java?如何使用 Java 编辑 MS Word 文档?
【发布时间】:2014-11-18 21:21:57
【问题描述】:

我确实有几个 Word 模板,我的要求是使用 Java 根据用户输入替换文档中的一些单词/占位符。我尝试了很多库,包括 docx4j 的 2-3 个版本,但没有任何效果,它们都没有做任何事情!

我知道以前有人问过这个问题,但我尝试了所有我知道的选项。那么,使用什么 java 库我可以“真正”替换/编辑这些模板?我更喜欢“易于使用/几行代码”的类型库。

我使用的是 Java 8,我的 MS Word 模板在 MS Word 2007 中。

更新

此代码是使用SO成员Joop Eggen提供的代码示例编写的

public Main() throws URISyntaxException, IOException, ParserConfigurationException, SAXException
    {
        URI docxUri = new URI("C:/Users/Yohan/Desktop/yohan.docx");
        Map<String, String> zipProperties = new HashMap<>();
        zipProperties.put("encoding", "UTF-8");

         FileSystem zipFS = FileSystems.newFileSystem(docxUri, zipProperties);

           Path documentXmlPath = zipFS.getPath("/word/document.xml");

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();

            Document doc = builder.parse(Files.newInputStream(documentXmlPath));

            byte[] content = Files.readAllBytes(documentXmlPath);
            String xml = new String(content, StandardCharsets.UTF_8);
            //xml = xml.replace("#DATE#", "2014-09-24");
            xml = xml.replace("#NAME#", StringEscapeUtils.escapeXml("Sniper"));

            content = xml.getBytes(StandardCharsets.UTF_8);
            Files.write(documentXmlPath, content);
    }

但是这会返回以下错误

java.nio.file.ProviderNotFoundException: Provider "C" Not found

at: java.nio.file.FileSystems.newFileSystem(FileSystems.java:341) at java.nio.file.FileSystems.newFileSystem(FileSystems.java:341)

at java.nio.fileFileSystems.newFileSystem(FileSystems.java:276)

【问题讨论】:

  • 也许可以考虑(我会选择 Apache HWPF):*.com/questions/203174/…
  • @CsBalazsHungary:链接创建于 5 年前。那时还没有 Java 8。
  • MS Word 2007 已经是 .docx 了吗?因为该格式是完美的,您可以使用 java zip 文件系统,并更改 /word/content.xml。这些库不保证原始格式。
  • @Sniper 遗憾的是它确实会导致问题:(
  • @JoopEggen:是的,它是 Docx。更喜欢看图书馆,你知道,很简单。

标签: java io ms-word


【解决方案1】:

对于 docx(带有 XML 和其他文件的 zip),可以使用 java zip 文件系统和 XML 或文本处理。

URI docxUri = ,,, // "jar:file:/C:/... .docx"
Map<String, String> zipProperties = new HashMap<>();
zipProperties.put("encoding", "UTF-8");
try (FileSystem zipFS = FileSystems.newFileSystem(docxUri, zipProperties)) {
    Path documentXmlPath = zipFS.getPath("/word/document.xml");

使用 XML 时:

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    factory.setNamespaceAware(true);
    DocumentBuilder builder = factory.newDocumentBuilder();

    Document doc = builder.parse(Files.newInputStream(documentXmlPath));
    //Element root = doc.getDocumentElement();

然后您可以使用 XPath 来查找这些位置,并重新写回 XML。

你甚至可能不需要 XML,但可以替换占位符:

    byte[] content = Files.readAllBytes(documentXmlPath);
    String xml = new String(content, StandardCharsets.UTF_8);
    xml = xml.replace("#DATE#", "2014-09-24");
    xml = xml.replace("#NAME#", StringEscapeUtils.escapeXml("Sniper")));
    ...
    content = xml.getBytes(StandardCharsets.UTF_8);
    Files.delete(documentXmlPath);
    Files.write(documentXmlPath, content);

为了快速开发,将 .docx 的副本重命名为具有 .zip 文件扩展名的名称,然后检查文件。

File.write 应该已经应用了 StandardOpenOption.TRUNCATE_EXISTING,但我添加了Files.delete,因为发生了一些错误。见 cmets。

【讨论】:

  • 感谢您的回复。您的意思是将我的 docx 的扩展名更改为 .zip ?
  • hmm.. 这可以与包含图像和表格的 word docx 一起使用吗?
  • 你看起来是个很棒的人,我会测试并回复你。
  • 图片在 /media 中。不,不是那么好,每个人都有经验,现在给你。这里的聪明之处不是使用 java 的 ZipFile,而是使用“jar:file:”URI 中的 java FileSystem。然后可以将图像文件复制到 docx 中,只需 Files.copy 等。
  • The images are in /media 你是什么意思?我的模板中有图像和表格。我不想通过 Java 触摸它们中的任何一个,只想知道您的示例是否可以读取文件,替换“文本”并按原样写回;这意味着,在不破坏图像的情况下,文档中的表格,当然还有替换的文本。
【解决方案2】:

试试Apache POIPOI 可以与 docdocx 一起使用,但 docx 记录更多,因此对它的支持更好。

UPD:您可以使用XDocReport,它使用 POI。另外我推荐使用xlsx作为模板,因为它更适合more documented

【讨论】:

  • 能否提供一个示例链接?
  • 1) 在适当的时候从发行版、读取源(POI 和 XDocReport)和谷歌搜索获得足够的样本。
【解决方案3】:

我在这个问题上花了几天时间,直到我发现文件系统实例上的try-with-resources 出现了差异,它出现在 Joop Eggen 的 sn-p 中,但没有问题 sn-p:
@ 987654323@
如果没有这样的try-with-resources 块,FileSystem 资源将不会关闭(如Java tutorial 中所述),并且不会修改 word 文档。

【讨论】:

    【解决方案4】:

    退一步说,有大约 4 种不同的方法来编辑单词/占位符:

    • MERGEFIELD 或 DOCPROPERTY 字段(如果您在 docx4j 中遇到此问题,那么您可能没有正确设置输入 docx)
    • content control databinding
    • 文档表面的变量替换(在 DOM/SAX 级别,或使用库)
    • 用 XHTML 做东西,然后import that

    在选择一个之前,你应该决定你是否还需要能够处理:

    • 重复数据(例如添加表格行)
    • 有条件的内容(例如,存在或不存在的整个段落)
    • 添加图片

    如果您需要这些,那么 MERGEFIELD 或 DOCPROPERTY 字段可能已不存在(尽管您也可以使用 IF 字段,如果您能找到支持它们的库)。并且添加图像会使 DOM/SAX 操作如其他答案之一所倡导的那样,更加混乱且容易出错。

    需要考虑的其他事项是:

    • 您的作者:他们的技术水平如何?这对创作 UI 意味着什么?
    • 您提到的用于变量替换的“用户输入”是给定的,还是获得它是您正在解决的问题的一部分?

    【讨论】:

      【解决方案5】:

      请尝试这个来编辑或替换文档中的单词

      public class UpdateDocument {
      
          public static void main(String[] args) throws IOException {
      
              UpdateDocument obj = new UpdateDocument();
      
              obj.updateDocument(
                        "c:\\test\\template.docx",
                        "c:\\test\\output.docx",
                        "Piyush");
          }
      
          private void updateDocument(String input, String output, String name)
              throws IOException {
      
              try (XWPFDocument doc = new XWPFDocument(
                      Files.newInputStream(Paths.get(input)))
              ) {
      
                  List<XWPFParagraph> xwpfParagraphList = doc.getParagraphs();
                  //Iterate over paragraph list and check for the replaceable text in each paragraph
                  for (XWPFParagraph xwpfParagraph : xwpfParagraphList) {
                      for (XWPFRun xwpfRun : xwpfParagraph.getRuns()) {
                          String docText = xwpfRun.getText(0);
                          //replacement and setting position
                          docText = docText.replace("${name}", name);
                          xwpfRun.setText(docText, 0);
                      }
                  }
      
                  // save the docs
                  try (FileOutputStream out = new FileOutputStream(output)) {
                      doc.write(out);
                  }
      
              }
      
          }
      
      }
      

      【讨论】: