尝试了解从 Apache poi 保存对 Word 文档的更改

arc*_*rcy 4 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();
  }
  
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

Axe*_*ter 7

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

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

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

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

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

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

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

所以我要做的是:

...
XWPFDocument doc = new XWPFDocument(new FileInputStream(source));
...
FileOutputStream fos = new FileOutputStream(destination);
doc.write(fos);
fos.close();
doc.close();
...
Run Code Online (Sandbox Code Playgroud)