java - 试图了解从 Apache poi 保存对 Word 文档的更改

标签 java apache-poi

我有一个 Word 文档 (docx);我想对该文档进行更改并将结果另存为另一个文件,将原始文件留在原处。我有以下代码说明我当前的问题:

package sandbox.word.doccopy;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

public class CopyTest
{
  public static void main(String[] args) throws Exception
  {
    String sourceFilename      = "CopyTestSource.docx";
    String destinationFilename = "CopyTestResult.docx";
    
    CopyTest docCopy = new CopyTest();
    docCopy.copyTesting(sourceFilename, destinationFilename);
    System.out.println("done");
  }
  
  public void copyTesting(String source, String destination)
      throws IOException, InvalidFormatException
  {
    XWPFDocument doc = new XWPFDocument(OPCPackage.open(source));
    // for each paragraph that has runs, 
    // put an exclamation at the end of the first run.
    for (XWPFParagraph par : doc.getParagraphs())
    {
      List<XWPFRun> runs = par.getRuns();
      if (runs.size() > 0) 
      { XWPFRun run = par.getRuns().get(0);
        String text = run.getText(0);
        text = text + "!";
        run.setText(text, 0);
      }
    }
    
//    FileOutputStream fos = new FileOutputStream(destination);
//    doc.write(fos);
//    fos.close();
    doc.close();
  }
  
}

我用三种方法运行它,更改类文件底部的注释行。如您所见,有 3 行使用目标文件名创建文件输出流、写入并关闭它,还有 1 行仅关闭当前文档。

如果我注释掉第 3 行并保留第 1 行,则不会向当前文档写入任何更改(当然,也不会创建副本文档)。

如果我保留所有 4 行未注释,则会创建包含更改的副本文档,并且更改也会写入源文档。

如果我注释掉第 4 行,我会得到一个有更改的目标文档,而源文档保持不变。

最后一个是我想要的,我可以编写我的代码来做到这一点。但我希望在更改后关闭文档会更改它或不更改它,并且更改它不取决于我是否已将更改写入另一个文件。

任何人都可以阐明这一点吗?

最佳答案

罪魁祸首是:XWPFDocument doc = new XWPFDocument(OPCPackage.open(source));。特别是这个:OPCPackage.open(source)

同时 static OPCPackage open(java.lang.String path) OPCPackage 从具有读/写权限的文件路径 path 的基础文件中打开。另外,它直接连接到底层文件。这可以节省一些内存,但也有缺点,正如您现在将要看到的那样。

XWPFDocument 中的所有更改都在该 OPCPackage 中进行,但首先在随机存取存储器中进行。

在调用 doc.write 时,调用 POIXMLDocument.write(java.io.OutputStream stream) ,首先底层的 OPCPackage 得到更新。然后更改的 OPCPackage 通过给定的 OutputStream 流 保存在目标文档中。因此,如果不调用 doc.write,文件中的任何内容都不会更改,只会保留在随机存取存储器中。

然后 doc.close() 也被调用 OPCPackage.close被调用。这将关闭打开的可写包并保存其内容。由于 OPCPackage 直接连接到底层文件,它将内容保存到该文件中。这就是将更改也写入源文档的原因。

这应该可以解释您的观察结果。

XWPFDocument 也提供构造函数 XWPFDocument(java.io.InputStream is) .这在内部调用 OPCPackage.open(java.io.InputStream in) .这将打开 InputStream 中的 OPCPackageOPCPackage 仅在随机存取存储器中,并且独立于源文件。这会使用更多内存,因为整个 OPCPackage 需要在随机存取内存中,但 OPCPackage.close 不会导致源文件发生变化。

所以我会做的是:

...
XWPFDocument doc = new XWPFDocument(new FileInputStream(source));
...
FileOutputStream fos = new FileOutputStream(destination);
doc.write(fos);
fos.close();
doc.close();
...

关于java - 试图了解从 Apache poi 保存对 Word 文档的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71285213/

相关文章:

java - Apache Velocity 如果不为空

java - 如何在关系数据库中保留大字符串字段的编辑历史

java - Python3 Opencv3 Ubuntu 15 - 出现错误

java - 使用Java apache POI填充Excel行中的背景颜色

java - 如何隐藏数据透视表中而不是工作表中的特定列?

java.lang.IllegalStateException : Cannot get a numeric value from a text cell Exception while uploading excel sheet to server 错误

java - 使用 POI 的 XSSF 和 SAX(事件 API)读取 Excel 工作表

java - 为什么 Optional<T> 不实现 Supplier<T>?

java - 如何安全地同时修改 Java HashMaps 中的值?

java - ApachePOI : NoClassDefFoundError : org. apache.poi.openxml4j.exceptions.invalidFormatException