Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why won't Apache POI OPCPackage close() method save/write contents to open writable file?

Using the following code, cell value changes made to my Excel Spreadsheet are not saved:

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

ModifyWorkbook();

pkg.close();

The following is a workaround I wrote, but I don't understand why it's necessary.

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

The javadocs and guides on the subject indicate that the .close() method should save and close. Checks on the values of the cells modified, prior to closing, indicate that the changes ARE made, but the pkg.close() method alone is not sufficient to write those changes to the file when closed.

like image 918
DanielReid Avatar asked Apr 19 '13 15:04

DanielReid


2 Answers

A call to OPCPackage.close() will close the underlying OOXML file structure. What it won't do is cause XSSF (or any of the X??F formats) to write their changes out. So, you'll write out an un-changed file....

If you're doing low level modifications, you can open the OPCPackage, make changes, then call close to write them out. If you're doing high level stuff, you open the OPCPackage, work with the UserModel code to make changes, then ask the usermodel to write out its changes. It's this last step that's important.

Your current code is somewhat like:

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

Reading the file in and modifying the high level objects isn't enough, you also need to tell the high level objects to write out the changes. (The high level objects cache things in memory for performance reasons)

like image 73
Gagravarr Avatar answered Nov 14 '22 23:11

Gagravarr


Calling close on an (XSSF)Workbook will call OPCP.close which javadoc states:

Calling myWorkbook.close() should write the changes to file it was open from.

However, the current version (3.15) seems to have problems doing so. The reason itself is unknown to me, but I discovered that calling myWorkbook.write() will commit not only the changes to the output stream, but also to your current instance after calling close.

A reasonable workaround could therefore be:

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

Here done with Guavas ByteStreams. Feel free to use other null output stream implementations.

like image 26
Dennis Avatar answered Nov 15 '22 01:11

Dennis