为什么Apache POI OPCPackage close()方法不能保存/写入打开可写文件的内容?

时间:2013-04-19 15:28:09

标签: java excel apache-poi xssf

使用以下代码,不会保存对Excel电子表格所做的单元格值更改:

OPCPackage pkg = OPCPackage.open(inputFile);
XSSFWorkbook wb = new XSSFWorkbook(pkg);

ModifyWorkbook();

pkg.close();

以下是我写的解决方法,但我不明白为什么这是必要的。

OPCPackage pkg = OPCPackage.open(inputFile);
XSSFWorkbook wb = new XSSFWorkbook(pkg);

ModifyWorkbook();

File tmp = File.createTempFile("tempSpreadsheet", ".xlsx");
FileOutputStream newExcelFile = new FileOutputStream(tmp);
wb.write(newExcelFile);
newExcelFile.close();
tmp.deleteOnExit();
pkg.close();

主题上的javadoc和指南表明.close()方法应该保存并关闭。在关闭之前检查已修改单元格的值是否表明已进行更改,但单独使用pkg.close()方法不足以在关闭时将这些更改写入文件。

3 个答案:

答案 0 :(得分:2)

OPCPackage.close()的调用将关闭基础OOXML文件结构。它不会做的是导致XSSF(或任何X ?? F格式)写出他们的更改。所以,你会写出一个未更改的文件....

如果您正在进行低级别修改,则可以打开OPCPackage,进行更改,然后调用close将其写出来。如果您正在执行高级操作,则打开OPCPackage,使用UserModel代码进行更改,然后请求usermodel 写出其更改。这是最后一步,这很重要。

您当前的代码有点像:

File f = new File("test.txt");
Sting s = readFileCompletely(f);
s = s.replaceAll("foo", "bar");
f.close();
// Why hasn't the file contents changed?

读入文件并修改高级对象是不够的,还需要告诉高级对象写出更改。 (出于性能原因,高级对象会将内容缓存在内存中)

答案 1 :(得分:1)

在(XSSF)工作簿上调用close将调用OPCP.close,其中javadoc声明:

  

调用myWorkbook.close()应该将更改写入文件   从...开始。

但是,当前版本(3.15)似乎遇到了问题。原因本身对我来说不了解,但我发现调用myWorkbook.write()不仅会提交对输出流的更改,还会在调用close之后提交给当前实例。

因此,合理的解决方法可能是:

try (OutputStream bos = ByteStreams.nullOutputStream()){
    workbook.write(bos);
    workbook.close();
} catch (IOException e) {
    log.error("Could not write Output File", e);
}

这里完成了Guavas ByteStreams。随意使用other null输出流实现。

答案 2 :(得分:0)

在最新版本的POI中,如果默认构造函数正在创建org.apache.poi.xssf.usermodel.XSSFWorkbook的对象,则无需手动进行操作。阅读其默认的构造方法

public XSSFWorkbook() {
        super(newPackage());
        onWorkbookCreate();
    }
protected static OPCPackage newPackage() {
        try {
            OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
            // Main part
            PackagePartName corePartName = PackagingURIHelper.createPartName(XSSFRelation.WORKBOOK.getDefaultFileName());
            // Create main part relationship
            pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
            // Create main document part
            pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());

            pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);

            return pkg;
        } catch (Exception e){
            throw new POIXMLException(e);
        }
    }