0

I copy XSSFWorkbook to SXSSFWorkbook. In this process, I create the new workbooks I use like this:

XSSFWorkbook readOnlyWb = (XSSFWorkbook) WorkbookFactory.create(f, null, true);
SXSSFWorkbook writeOnlyWb = new SXSSFWorkbook();

When writing to disk, the content type of the SXSSFWorkbook is always

/xl/workbook.xml - Content Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml

This is no issue, if the source workbook is an .xlsx file which has the same content type.

But I also have .xlsm files which I want to copy and they should remain to be .xlsm files. These files have the content type

/xl/workbook.xml - Content Type: application/vnd.ms-excel.sheet.macroEnabled.main+xml

So basically the issue I have is, to dynamically get the right content type for each file type while using SXSSFWorkbook.

How can I change the content type for the SXSSFWorkbook dynamically?

XtremeBaumer
  • 6,275
  • 3
  • 19
  • 65
  • 1
    You cannot create a `SXSSFWorkbook` having content type of `*.xlsm` from scratch, as well as you cannot create a `XSSFWorkbook` having content type of `*.xlsm` from scratch. Why not using the `*.xlsm` as template `workbook` in [new SXSSFWorkbook(workbook)](https://poi.apache.org/apidocs/dev/org/apache/poi/xssf/streaming/SXSSFWorkbook.html#SXSSFWorkbook-org.apache.poi.xssf.usermodel.XSSFWorkbook-). That would work. – Axel Richter Sep 03 '19 at 08:36
  • The issue with that is that I need to alter initial rows of the template, which is not allowed/possible to do with `SXSSFWorkbook`. From docs: `What is not supported: Access initial cells and rows in the template.` as well as `Override existing cells and rows.` – XtremeBaumer Sep 03 '19 at 08:49
  • That's true. That's why I asked. But there is not any method for creating `*.xlsm` from scratch in `apache poi`. The content type one could change using ugly low level manipulating the `[Content_Types].xml`. But what about the `vbaProject.bin` and it's relations? Would be very laborious. – Axel Richter Sep 03 '19 at 08:57
  • Why not alter the initial rows of the template using `XSSFWorkbook` at first and then creating the `SXSSFWorkbook` from that changed `XSSFWorkbook` then for streaming in the rest of the data? – Axel Richter Sep 03 '19 at 09:39
  • I am not allowed to perform any changes on the original files (that's why I open them in read-only mode). I am actually checking if the macros even need to be retained, which would solve the issue at hand if not. And as its not possible to copy macros, there is no sense in creating an empty `*.xlsm` template. Of course I could copy around the files a few times so to not alter the initial files, but I would like it to be as performant as possible – XtremeBaumer Sep 03 '19 at 09:58
  • The `XSSFWorkbook` from which the `SXSSFWorkbook` is created is an object in memory. There is no need to change the file. – Axel Richter Sep 03 '19 at 10:01

1 Answers1

1

You cannot create a SXSSFWorkbook having content type of *.xlsm from scratch, as well as you cannot create a XSSFWorkbook having content type of *.xlsm from scratch. But you can create a XSSFWorkbook form a *.xlsm file and this will preserve all the contents inclusive the content types and the vbaProject.bin VBA macro project as well. Then you can create the SXSSFWorkbook from that template XSSFWorkbook using constructor SXSSFWorkbook(XSSFWorkbook workbook). After that the SXSSFWorkbook also will preserve all the contents.

If the need is changing some parts of the template XSSFWorkbook, then this must be done before the SXSSFWorkbook was created from that template. The SXSSFWorkbook is not able changing rows which already was in the template. This could be done in memory whithout changing the template file when the XSSFWorkbook is fully contained im memory. That is the case if it was created using a InputStream.

Following code shows that. It creates a XSSFWorkbookfrom a *.xlsm file using FileInputStream. Then it changes some parts of the template before it creates a SXSSFWorkbook from it. Then the big amout of date is streamed in in the SXSSFWorkbook. The result is a *.xlsm file having the correct content type and also having the vbaProject.bin VBA macro project form the template as well.

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.xssf.streaming.*;

public class CreateExcelSXSSFFromXLSM {

 public static void main(String[] args) throws Exception {

  XSSFWorkbook templateWorkbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("Workbook.xlsm"));
  Sheet sheet = templateWorkbook.getSheet("Sheet1");
  for (Row row : sheet) {
   for (Cell cell : row) {
    System.out.println(cell);
    if (cell.getColumnIndex() == 0) cell.setCellValue("changed in template");
   }
  }

  int lastRowInTemplate = sheet.getLastRowNum();

  SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(templateWorkbook); 
  SXSSFSheet sxssfSheet = sxssfWorkbook.getSheet("Sheet1");
  for (int r = lastRowInTemplate + 1; r < lastRowInTemplate + 10; r++) {
   SXSSFRow row = sxssfSheet.createRow(r);
   for (int c = 0; c < 10; c++) {
    SXSSFCell cell = row.createCell(c);
    cell.setCellValue("R" + (r+1) + "C" + (c+1));
   }
  }

  FileOutputStream out = new FileOutputStream("WorkbookNew.xlsm");
  sxssfWorkbook.write(out);
  out.close();
  sxssfWorkbook.close();
  sxssfWorkbook.dispose();
 }
}
Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • Seems there is a difference between using a `File` object and an `FileInputStream` to load an `XSSFWorkbook` I didn't know about. This being the `Last Modified` date is updated when using a `File`, but not when using a `FileInputStream` (that was the reason I use `WorkbookFactory.create(f, null, true)`). Thanks for pointing that one out! Its a great answer and if I need to retain macros, this will definitly be my base – XtremeBaumer Sep 03 '19 at 11:04
  • @XtremeBaumer: "Seems there is a difference between using a File object and an FileInputStream": Yes there is, see https://stackoverflow.com/questions/55929312/apache-poi-appending-data-to-xlsx-file-when-task-ran-twice/55937539#55937539 and https://stackoverflow.com/questions/46146161/apache-poi-fileinputstream-works-file-object-fails-nullpointerexception/46149469#46149469 – Axel Richter Sep 03 '19 at 11:08