2

I have an existing XLSM file, in which I try to write data with use of Apache POI.

String File = GlobalVariables.XLSM;

try {
    Workbook workbook;
    workbook = new XSSFWorkbook(OPCPackage.open(GlobalVariables.XLSM));
    Sheet sheet = workbook.getSheetAt(0);
    Row row = sheet.createRow(recordcount+5);
    Cell cell;

    cell = row.createCell(GlobalVariables.testID);
    cell.setCellValue(recordcount);

    FileOutputStream out = new FileOutputStream(new File(File));
    workbook.write(out);
    out.close();
    System.out.println("Data was written in XLSM");

} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (InvalidFormatException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

I got error message:

org.apache.poi.ooxml.POIXMLException: java.io.EOFException: Unexpected end of ZLIB input stream

Problem it not related to GZIPinStream and GZIPOutputStream.

UPDATE 2019.06.04.

I modified code, but still getting error:

try {
    FileInputStream file = new FileInputStream(GlobalVariables.XLSM);
    XSSFWorkbook workbook = new XSSFWorkbook(file);
    XSSFSheet sheet = workbook.getSheetAt(0);
    XSSFRow row = sheet.createRow(recordcount+4);
    Cell cell;

    cell = row.createCell(GlobalVariables.testID);
    cell.setCellValue(recordcount+1);

    file.close();

    FileOutputStream out = new FileOutputStream(new File(GlobalVariables.XLSM));
    workbook.write(out);
    out.flush();
    out.close();
    workbook.close();
    System.out.println("Data was written");

} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

Error message:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <logFileName>error066080_01.xml</logFileName>
    <summary>Error in file (C:\_privat\completeReport.xlsm)</summary>
    <removedRecords>
        <removedRecord>/xl/worksheets/sheet1.xml</removedRecord>
        <removedRecord>/xl/calcChain.xml</removedRecord>
    </removedRecords>
</recoveryLog>
plaidshirt
  • 5,189
  • 19
  • 91
  • 181
  • Possible duplicate of [Exception: Unexpected end of ZLIB input stream](https://stackoverflow.com/questions/24531089/exception-unexpected-end-of-zlib-input-stream) – XtremeBaumer Jun 03 '19 at 07:41
  • 1
    You cannot create macros for an .xlsm file, it is just not supported by apache-poi. You can only read and re-write an .xlsm file containing macros. Are you doing so? Is `GlobalVariables.XLSM` the path to an already existing .xlsm workbook? What is `File` in `FileOutputStream out = new FileOutputStream(new File(File));`? Is `GlobalVariables.testID` an `int` or `long`? – deHaar Jun 03 '19 at 07:46
  • @deHaar : GlobalVariables.XLSM contains path of .xlsm file. GlobalVariables.testID is an int. I try to update xlsm file in a loop in each iteration. – plaidshirt Jun 03 '19 at 07:56
  • Do you always open and close it inside the loop? Can you show us the entire method or even class with that loop? – deHaar Jun 03 '19 at 07:59
  • https://stackoverflow.com/a/49195332/7924858 – abhinavsinghvirsen Jun 03 '19 at 08:21
  • @abhinavxeon: I get this error: `java.lang.IllegalArgumentException: partName` -> `workbook.write(out);` – plaidshirt Jun 03 '19 at 11:41
  • @deHaar : XLSX is modified, but got error message, that it is broken and should be recovered. These items are removed: `/xl/worksheets/sheet1.xml /xl/calcChain.xml` – plaidshirt Jun 03 '19 at 11:50
  • Your code can leak the `FileOutputStream` resource. – Raedwald Jun 03 '19 at 11:54
  • @Raedwald is nearly right, your `FileOutputStream` is closed properly, but your `workbook` is not, which can lead to a broken file. – deHaar Jun 03 '19 at 13:19
  • @deHaar : I updated code. – plaidshirt Jun 04 '19 at 07:46

1 Answers1

2

Nearly every time you stumble upon an error opening a workbook that you created by code, the reason is some not properly closed resource. Unfortunately, I experienced that a lot of times ;-)

In my cases, I could resolve the issue by the following sequence of actions (order matters):

  1. write the workbook: workbook.write(out);
  2. force the FileOutputStream to empty all used buffers: out.flush();
  3. close the FileOutputStream: out.close();
  4. close the workbook: workbook.close();

This is how I would add the missing actions to your code:

    try {
        Workbook workbook;
        workbook = new XSSFWorkbook(OPCPackage.open(GlobalVariables.XLSM));
        Sheet sheet = workbook.getSheetAt(0);
        Row row = sheet.createRow(recordcount + 5);
        Cell cell;

        cell = row.createCell(GlobalVariables.testID);
        cell.setCellValue(recordcount);

        FileOutputStream out = new FileOutputStream(new File(File));
        // 1. write the workbook
        workbook.write(out);
        // 2. force the FileOutputStream to write everything out before closing it
        out.flush();
        // 3. then close the FileOutputStream
        out.close();
        // 4. finally close the workbook
        workbook.close();
        System.out.println("Data was written in XLSM");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (InvalidFormatException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

Please note that flushing the FileOutputStream might not always be necessary, but it was mentioned in several best-practice tutorials concerning apache-poi, so I decided to use it my code.

This works for me, I hope it will do so for you as well.

EDIT (OP asked for my example code in a comment below)
This is an independent example for reading and extending an XSSFWorkbook. It is all just in the main method, but at least commented ;-)
You have to change the path to the workbook, which is basically a String in this example and becomes a java.nio.Path afterwards.

public class PoiMain {

    public static void main(String[] args) {
        /*
         * Setup:
         * An existing xlsx file with a first sheet containing 6 columns and 1 row.
         * The row has 6 filled cells with the values
         * cell 1 (index 0): There
         * cell 2 (index 1): is
         * cell 3 (index 2): a
         * cell 4 (index 3): house
         * cell 5 (index 4): in
         * cell 6 (index 5): New Orleans
         * 
         * Task:
         * Write the words "they", "call", "it", "the", "rising", "sun"
         * in the cells below.
         */

        // define the (correct) path to the workbook
        String pathToFile = "Y:\\our\\path\\to\\the\\Test-Workbook.xlsx"; // you can use an xlsm here, too
        // create a Path object
        Path filePath = Paths.get(pathToFile);
        // declare a workbook
        XSSFWorkbook workbook;

        try {
            /*
             * READING from the .xlsx file:
             */

            FileInputStream in = new FileInputStream(filePath.toFile());
            workbook = XSSFWorkbookFactory.createWorkbook(in);
            XSSFSheet sheet = workbook.getSheetAt(0);

            // read all the cells of the first row and print their content
            for (int i = 0; i < sheet.getPhysicalNumberOfRows(); i++) {
                XSSFRow row = sheet.getRow(i);
                for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) {
                    XSSFCell cell = row.getCell(j);
                    System.out.println(cell.getStringCellValue());
                }
            }

            /*
             * WRITING to the .xlsx file already read
             */

            // create some meaningful words to be added to some cells in the workbook
            List<String> wordsToBeWritten = Arrays.asList("they", "call", "it", "the", "rising", "sun");

            FileOutputStream out = new FileOutputStream(filePath.toAbsolutePath().toString());
            sheet = workbook.getSheetAt(0);
            XSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
            // create new cells and write the words into them
            for (int i = 0; i < wordsToBeWritten.size(); i++) {
                XSSFCell cell = row.createCell(i);
                cell.setCellValue(wordsToBeWritten.get(i));
            }
            // close the FileInputStream
            in.close();
            // write the workbook using the FileOutputStream
            workbook.write(out);
            // force the FileOutputStream to write everything until it is empty
            out.flush();
            // close the FileOutputStream
            out.close();
            // close the workbook.
            workbook.close();
        } catch (FileNotFoundException e) {
            System.err.println(
                    "The file \"" + filePath.toAbsolutePath().toString() + "\" could not be found.");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("Error while reading the file \"" + filePath.toAbsolutePath().toString() + "\"");
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            System.out.println(
                    "The file \"" + filePath.toAbsolutePath().toString() + "\" has an invalid format(ting)");
            e.printStackTrace();
        } catch (EmptyFileException e) {
            System.err.println("The supplied file \"" + filePath.toAbsolutePath().toString() + "\" is empty.");
            e.printStackTrace();
        }
}
deHaar
  • 17,687
  • 10
  • 38
  • 51
  • 1
    I get error message again: `org.apache.poi.ooxml.POIXMLException: java.io.EOFException: Unexpected end of ZLIB input stream` for line `workbook.write(out);` and 0 kB file is produced. – plaidshirt Jun 04 '19 at 07:37
  • @plaidshirt If it cannot do `workbook.write(out);` already, then the reason might be your `FileOutputStream`. Please have a look at `FileOutputStream out = new FileOutputStream(new File(File));`. What value is `File` holding (and why is it capitalized)? – deHaar Jun 04 '19 at 08:04
  • I changed it to `GlobalVariables.XLSM`, because it holds the same value, path to xlsm file. – plaidshirt Jun 04 '19 at 08:10
  • @plaidshirt Then you have to make sure it is not opened while the program tries to access it. Otherwise please try this with an .xlsx file and find out if the file format is a problem. – deHaar Jun 04 '19 at 08:12
  • @plaidshirt A difference to my code (just checked if it correctly reads an .xlsm-file and writes to it) is the use of the `OPCPackage`. I don't use it, instead of the line `workbook = new XSSFWorkbook(OPCPackage.open(GlobalVariables.XLSM));`, I have this one: `workbook = XSSFWorkbookFactory.createWorkbook(in);`. Another difference is that I close the `FileInputStream` directly before writing the workbook, but that should not be a problem. – deHaar Jun 04 '19 at 08:23
  • I modified that too, but size of file still decreased during processing, originally it is 130 kB and I got a 105 kB file, which is corrupted. – plaidshirt Jun 04 '19 at 08:28
  • @plaidshirt How do you determine the `recordCount`? The reason for the error may be unexpected (by apache-poi) manipulation of the file. You can check how many rows there are in the file by `row.getPhysicalNumberOfCells();`. I wouldn't create a new row that has an index larger than `row.getPhysicalNumberOfCells() + 1`, because *row gaps* may cause problems. Same with cells… – deHaar Jun 04 '19 at 08:43
  • recordCount is increased in each method calls, it provides an ID column. – plaidshirt Jun 04 '19 at 09:52
  • Could you please edit answer with code you are using? – plaidshirt Jun 04 '19 at 10:02
  • @plaidshirt I added an independent example that reads a workbook and writes a new line. – deHaar Jun 04 '19 at 10:25