Apache poi method to write data to an existing workbook - java

This is my class to read and write existing excel files. I have been calling these functions in the main class by passing the filePath and fileName.
public class NewExcelFile {
Workbook workbook;
/******* Methods *******/
// returns a workbook on giving the excel file's path and name
public Workbook readExcel(String filePath, String fileName) {
// Create object of File class to open xlsx file
File file = new File(filePath + "\\" + fileName);
// Create an object of FileInputStream class to read excel file
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
System.out.println("Error: Unable to find " + fileName + " in "
+ filePath);
e.printStackTrace();
}
Workbook workbook = null;
// Find the file extension by spliting file name in substring and
// getting only extension name
String fileExtensionName = fileName.substring(fileName.indexOf("."));
// Check condition if the file is xlsx file
if (fileExtensionName.equals(".xlsx")) {
// If it is xlsx file then create object of XSSFWorkbook class
try {
workbook = new XSSFWorkbook(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// Check condition if the file is xls file
else if (fileExtensionName.equals(".xls")) {
// If it is xls file then create object of XSSFWorkbook class
try {
workbook = new HSSFWorkbook(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
this.workbook = workbook;
return workbook;
}
public void writeExcel(String filePath, String fileName, String sheetName,
String dataToWrite, int rowno) {
System.out.println("WriteExcel" + filePath + " " + fileName + " "
+ sheetName + " " + dataToWrite + " " + rowno);
Workbook newWorkbook = readExcel(filePath, fileName);
Sheet sheet = newWorkbook.getSheet(sheetName);
System.out.println("Sheet: " + sheet.getSheetName());
Cell resultcell;
******resultcell = sheet.getRow(rowno).createCell(8);
resultcell.setCellType(Cell.CELL_TYPE_STRING);
resultcell.setCellValue(dataToWrite);
CellStyle style = workbook.createCellStyle();
if (dataToWrite == "P") {
style.setFillBackgroundColor(IndexedColors.GREEN.getIndex());
style.setFillPattern(CellStyle.ALIGN_FILL);
resultcell.setCellStyle(style);
} else if (dataToWrite == "F") {
style.setFillBackgroundColor(IndexedColors.RED.getIndex());
style.setFillPattern(CellStyle.ALIGN_FILL);
resultcell.setCellStyle(style);
}
// Create an object of FileOutputStream class to create write data in
// excel file
File file = new File(filePath + "\\" + fileName);
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
} catch (FileNotFoundException e) {
System.out.println("File not found");
e.printStackTrace();
}
// write data in the excel file and close output stream
try {
workbook.write(outputStream);
outputStream.close();
} catch (IOException e) {
System.out.println("Error in writing to file");
e.printStackTrace();
}
}
When I get a workbook in the main using readExcel and call this function:
Row row = testScriptsSheet.getRow(24);
I get the correct row and am able to call all functions on this row.But for the exact same row in the exact same sheet in the writeExcel(), I get a null pointer exception(the line preceded by *** in the code above). getRow() gives me null here. What am I doing wrong here?
Also, should I keep workbook as a data member and domyNewExcelFile.workbook whenever I need it or keep it as a variable returned from the readExcel in the main class?
Also I was wondering what is happening now that I am not closing the inputStream at the end of the readExcel function. I get the same error whether I close the inputStream or not.
EDIT - Adding the main function
public class NewDriver {
public static void main(String[] args) {
System.out.println("Starting the framework");
// initialise the workbook
NewExcelFile testExecution = new NewExcelFile();
testExecution.readExcel(System.getProperty("user.dir") + "\\",
"abc.xlsx");
// initialise sheets of workbook
Sheet testSuiteSheet = testExecution.workbook.getSheet("TestSuite");
Sheet testScriptsSheet = testExecution.workbook.getSheet("TestCases");
Row row = testScriptsSheet.getRow(24);//gives the correct row
//calling writeExcel gives npe in that line
}
}
}
}

From the docs the getRow(int) method:
Returns the logical row (not physical) 0-based. If you ask for a row
that is not defined you get a null. This is to say row 4 represents
the fifth row on a sheet.
So when a row is not defined, you must first create the row and then create the cell.

As per my understanding it seems a conceptual error. Before calling WriteExcel() method, all the changes that you have made in main method are in buffer, not written in the excel sheet/workbook present in your harddisk. But in WriteExcel() method you are not passing the sheet or workbook you kept in buffer, but the address of the one physically present in the hard drive. So any change you have done in main function is not there, hence showing null pointer exception.
e.g. I have one workbook in say my D Drive, having value 1 in A0. Now I have programmatically made it 2 but not perform the write operation, and put the execution on hold. Meanwhile I went to my D drive and open the sheet there will be 1 not 2, as the updated value is in buffer, till I have perform write operation on that workbook.
Suggestion: Instead of passing the address of the workbook, why not you just pass the workbook you have used in main method.
UPDATE: Not the main method but readExcel(String filePath, String fileName) method actually.

Related

Why can't I prevent Apache POI from altering the source file?

I am opening an Excel file (the source) in Java using an Apache POI Workbook, altering data in a certain set of cells, saving the Workbook to a separate file, then closing the Workbook (because the documentation states to close the Workbook, even if it is read-only).
POI alters the data in the source Excel file every time. I have tried a few different methods to prevent this according to recommendations from the POI documentation, but these methods fail.
Here are two attempts that should work in theory, but do not.
Attempt 1 - set the source file to read only
File file = new File("{path-to-existing-source-file}");
file.setReadOnly();
Workbook workbook = WorkbookFactory.create(file); // throws a FileNotFoundException
A FileNotFoundException for "Access is denied" is thrown at WorkbookFactory.create(file):
java.io.FileNotFoundException: {path-to-source-file-that-exists} (Access is denied)
at java.io.RandomAccessFile.open0(Native Method)
at java.io.RandomAccessFile.open(RandomAccessFile.java:316)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:243)
at org.apache.poi.poifs.nio.FileBackedDataSource.newSrcFile(FileBackedDataSource.java:158)
at org.apache.poi.poifs.nio.FileBackedDataSource.<init>(FileBackedDataSource.java:60)
at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:224)
at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:172)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:298)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:271)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:252)
at com.stackoverflow.MyClass(MyClass.java:71)
The source file exists, and it is validly read-only.
Attempt 2 - use the POI API constructor which allows read-only to be explicitly set
File file = new File("{path-to-existing-source-file}");
Workbook workbook = WorkbookFactory.create(file, null, true); // true is read-only
// dataBean is just a container bean with the appropriate reference values
Sheet sheet = workbook.getSheet(dataBean.getSheetName());
Row row = sheet.getRow(dataBean.getRowNumber());
Cell cell = row.getCell(dataBean.getColumnNumber());
cell.setCellValue(dataBean.getValue());
// target is another File reference
OutputStream outStream = new FileOutputStream(new File("path-to-target-file"));
workbook.write(outStream); // throws InvalidOperationException
An InvalidOperationException is thrown during the write call:
Caused by: org.apache.poi.openxml4j.exceptions.InvalidOperationException:
Operation not allowed, document open in read only mode!
at org.apache.poi.openxml4j.opc.OPCPackage.throwExceptionIfReadOnly(OPCPackage.java:551)
at org.apache.poi.openxml4j.opc.OPCPackage.removePart(OPCPackage.java:955)
at org.apache.poi.openxml4j.opc.PackagePart.getOutputStream(PackagePart.java:531)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.commit(XSSFWorkbook.java:1770)
at org.apache.poi.ooxml.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:463)
at org.apache.poi.ooxml.POIXMLDocument.write(POIXMLDocument.java:236)
at com.stackoverflow.MyClass(MyClass.java:90)
"Operation not allowed, document open in read only mode!". Of course it is set to read-only; I don't want the source written to, I just want all the data to go to a new target.
What can I set or change to not alter the source when using POI?
Our current workaround is to create a duplicate source file, but that is not a good solution.
I had the same problem and solved it by using a FileInputStream instead of a File.
Workbook workbook = WorkbookFactory.create(file);
becomes:
Workbook workbook = WorkbookFactory.create(new FileInputStream(file));
You need to have two workbooks, one where you get the datafrom (read) and another one you write to.
Look man, this is how I did a few months ago, please notice that I use .write() on the second workbook (hssfWorkbookNew), not the one im using to read the data from, read it carefully. This code is just for getting first sheet of a XLS excel and copying it to a new file.
// this method generates a new excelFile based on the excelFile he receives
public void generarXLS(File excelFile, File excelNewFile) {
InputStream excelStream = null;
OutputStream excelNewOutputStream = null;
try {
excelStream = new FileInputStream(excelFile);
excelNewOutputStream = new FileOutputStream(excelNewFile);
// Representation of highest level of excel sheet.
HSSFWorkbook hssfWorkbook = new HSSFWorkbook(excelStream);
HSSFWorkbook hssfWorkbookNew = new HSSFWorkbook();
// Chose the sheet that we pass as parameter.
HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(0);
// Create new sheet we are gonna use.
HSSFSheet hssfSheetNew = hssfWorkbookNew.createSheet("Copy-Copia");
// Create new sheet where we will copy the data
// Object that allow us to read a row from the sheet and extract the data from the cells
HSSFRow hssfRow;
HSSFRow hssfRowNew; // for hssfSheetNew
// Initialize the object that reads value of cell
HSSFCell cellNew;
// Get number of rows of the sheet
int rows = hssfSheet.getLastRowNum();
String cellValue;
// Style of the cell border, color background and pattern (fill pattern) used.
CellStyle style = hssfWorkbookNew.createCellStyle();
// Definition of the font of the cell.
// Iterate trhough all rows to get the cells and copy them to the new sheet
for (Row row : hssfSheet) {
hssfRowNew = hssfSheetNew.createRow(row.getRowNum());
if (row.getRowNum() > 999999) {
break;
}
for (Cell cell : row) {
cellValue = (cell.getCellType() == CellType.STRING) ? cell.getStringCellValue()
: (cell.getCellType() == CellType.NUMERIC) ? "" + cell.getNumericCellValue()
: (cell.getCellType() == CellType.BOOLEAN) ? "" + cell.getBooleanCellValue()
: (cell.getCellType() == CellType.BLANK) ? ""
: (cell.getCellType() == CellType.FORMULA) ? "FORMULA"
: (cell.getCellType() == CellType.ERROR) ? "ERROR" : "";
cellNew = hssfRowNew.createCell(cell.getColumnIndex(), CellType.STRING);
cellNew.setCellValue(cellValue);
}
}
// NOTICE how I write to the new workbook
hssfWorkbookNew.write(excelNewOutputStream);
hssfWorkbook.close();
hssfWorkbookNew.close();
excelNewOutputStream.close();
JOptionPane.showMessageDialog(null, Constantes.MSG_EXITO, "Informacion", 1);
} catch (FileNotFoundException fileNotFoundException) {
JOptionPane.showMessageDialog(null, "file not found", "Error", 0);
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error processing the file", "Error", 0);
} finally {
try {
excelStream.close();
} catch (IOException ex) {
System.out.println("Error processing the file after closing it): " + ex);
}
}
}
I had to handle XSSF and HSSF; this is how it was accomplished:
void handle(File inFile, File outFile) throws IOException {
Workbook workbook = WorkbookFactory.create(inFile);
workbook.setMissingCellPolicy(MissingCellPolicy.RETURN_BLANK_AS_NULL); // LINE NOT REQUIRED
if (workbook instanceof XSSFWorkbook) {
handleXSSF((XSSFWorkbook) workbook, outFile);
} else if (workbook instanceof HSSFWorkbook) {
handleHSSF((HSSFWorkbook) workbook, outFile);
} else {
throw new IOException("Unrecognized Workbook Type " + workbook.getClass().getName());
}
}
void handleHSSF(HSSFWorkbook hWorkbook, File outFile) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(outFile);
hWorkbook.write(fos);
fos.close();
} finally {
try {
hWorkbook.close();
} catch (Exception ignore) {}
}
}
void handleXSSF(XSSFWorkbook xWorkbook, File outFile) throws IOException {
SXSSFWorkbook sWorkbook = new SXSSFWorkbook(xWorkbook, 100);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(outFile);
sWorkbook.write(fos);
fos.close();
} finally {
try {
sWorkbook.close();
} catch (Exception ignore) {}
try {
sWorkbook.dispose();
} catch (Exception ignore) {}
try {
xWorkbook.close();
} catch (Exception ignore) {}
}
}
Perhaps you could also just use the create signature
Workbook workbook = WorkbookFactory.create(new File("//server/path/file.ext"), null, true);
to ask POI to open the spreadsheet read only?

Force close an excel file before writing to it

I am writing to an existing excel file using Java, say this is fileA. (I'm using APACHE POI for this.)
It is possible that the excel fileA is opened by someone. It is saved in a shared folder accessed by a lot of people.
I want to avoid encountering
java.io.FileNotFoundException: (The process cannot access the file
because it is being used by another process)
Because no matter if the existing excel file is opened or not, I need to save the output of my Java app.
Upon researching, I think it is impossible to close fileA (opened by some other process/user, not by my Java App) within my Java code.
What I'm doing now is to create a new excel file, say fileB if fileA is currently opened. I'm using the code below.
File file = null;
FileOutputStream out = null;
int workbookNo = 0;
do{
String append = "";
if(workbookNo != 0){
append = "_Copy" + Integer.toString(workbookNo);
}
file = new File(filePath + "ValidateLock_" + dataDate + append + ".xlsx");
try{
out = new FileOutputStream(file);
workbookNo = 0;
}catch(FileNotFoundException e){
//e.printStackTrace();
workbookNo++;
}
}while(workbookNo != 0);
However, I'm getting the error below.
org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException: No
valid entries or contents found, this is not a valid OOXML (Office
Open XML) file
try like this :
try {
FileInputStream file = new FileInputStream(new File("C:\\update.xls"));
HSSFWorkbook workbook = new HSSFWorkbook(file);
HSSFSheet sheet = workbook.getSheetAt(0);
Cell cell = null;
//Update the value of cell
cell = sheet.getRow(1).getCell(2);
cell.setCellValue(cell.getNumericCellValue() * 2);
cell = sheet.getRow(2).getCell(2);
cell.setCellValue(cell.getNumericCellValue() * 2);
cell = sheet.getRow(3).getCell(2);
cell.setCellValue(cell.getNumericCellValue() * 2);
//Close the excel input file (inputstream)
file.close();
FileOutputStream outFile =new FileOutputStream(new File("C:\\update.xls"));
workbook.write(outFile);
//Close output excel file
outFile.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

Getting error "Your InputStream was neither an OLE2 stream, nor an OOXML stream" when created file through apache POI

I am trying to check if my excel file already exists. If it doesn't exists, I want to create a new one and if it exists I will delete it and create a new one. I wrote following program but I am getting error at line - workbook= WorkbookFactory.create(instream);
The error is->
java.lang.IllegalArgumentException: Your InputStream was neither an OLE2 stream, nor an OOXML stream
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:89)
at tryIng.main(tryIng.java:84)
Here is a program ->
try {
String filePath= "C:/Users/pritik/Desktop/t1.xlsx";
File file = new File(filePath);
filePath= file.getAbsolutePath();
xlFile = new File(filePath);
if(xlFile.exists() && !xlFile.isDirectory())
xlFile.delete(); //delete if file already exists.
xlFile.createNewFile();
inStream = new FileInputStream(xlFile);
workbook = WorkbookFactory.create(inStream); // I get error at this line
String sheetName="NewSheet";
Sheet sheet = workbook.createSheet(sheetName);
FileOutputStream fOut = new FileOutputStream(xlFile);
int i,j;
xRows = xTS.length;
xCols = xTS[0].length;
for(i =0;i<xRows;i++)
{
row = sheet.createRow(i);
for(j=0;j<xCols;j++)
{
cell = row.createCell(j);
cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(xTS[i][j]);
}
}
workbook.write(fOut);
fOut.flush();
fOut.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Don't create an empty file and try to read it, that won't work. An empty zero byte file is not valid, and can't be loaded Instead, have POI create an new file for you, which you will write later.
Change the code:
if(xlFile.exists() && !xlFile.isDirectory())
xlFile.delete(); //delete if file already exists.
xlFile.createNewFile();
inStream = new FileInputStream(xlFile);
workbook = WorkbookFactory.create(inStream);
To instead be:
if(xlFile.exists() && !xlFile.isDirectory())
xlFile.delete(); //delete if file already exists.
if (xlFile.toString().endsWith(".xls") {
workbook = new HSSFWorkbook();
} else {
workbook = new XSSFWorkbook();
}
Also, if you do want to read an existing file, don't use a stream if you have a file! See this bit of the POI docs for why not.

Java apache POI java.lang.IllegalArgumentException: Position 21504 past the end of the file

I don't know what am I doing wrong. I'm just reading and writing an Excel file, but always get this exception:
java.lang.IllegalArgumentException: Position 21504 past the end of the file
public class example {
public static void main(String[] args) throws Exception {
File destFile = new File("book.xls");
Workbook destBook = WorkbookFactory.create(destFile);
try {
FileOutputStream out = new FileOutputStream(destFile);
destBook.write(out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
book.xls exists and has the number "1" in each cell from A1 to L50.
You are attempting to write the Workbook back to the same file pathname from which it was read. It appears that WorkbookFactory.create doesn't "release resources" until the Workbook is closed.
Note that in order to properly release resources the Workbook should be closed after use.
When you create the FileOutputStream, you have effectively erased the existing file so that you can write the file data out. However, the Workbook must still rely on the original file being intact. Then, the data to write no longer exists.
You will need to write to a different temporary filename first. Use Apache POI 3.11 or later, so you can call close() on the Workbook, releasing resources.
Close the underlying input resource (File or Stream), from which the Workbook was read. After closing, the Workbook should no longer be used.
This means that the original file must exist until we're done writing it, so the write must be to another (temporary) file.
File srcFile = new File("book.xls");
File destFile = new File("booktemp.xls");
try {
Workbook destBook = WorkbookFactory.create(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
destBook.write(out);
out.close();
destbook.close(); // Available in Apache POI 3.11!
} catch (Exception e) {
e.printStackTrace();
}
Then you can delete the original file and rename the newly created temporary file to the original name.
boolean deleteSuccess = srcFile.delete();
boolean renameSuccess = destFile.renameTo(srcFile);
Pass FileInputStream object in create method instead of File object. It will work.
public static void main(String[] args) throws Exception
FileInputStream destFile = new FileInputStream("book.xls");
Workbook destBook = WorkbookFactory.create(destFile);
try {
FileOutputStream out = new FileOutputStream(destFile);
destBook.write(out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}

Avoid file contents in an excel from getting overwritten

I want to have just a single FileOutputStream that writes contents of my workbook to a file in my application, and create multiple worksheets within this excel. I am using Apache POI to read/write to my excel. I have the below method where I am doing this -
private static void writeToSpreadSheet(String test,Map<String,String> errorMap,Object object) throws IOException {
HSSFWorkbook workbook = new HSSFWorkbook()
HSSFSheet sheet = workbook.createSheet(test);
FileOutputStream fis = new FileOutputStream("output/test.xls");
//do stuff and finally write workbook contents to a file
workbook.write(fis);
if (fis != null)
fis.close();
}
The problem I am facing here is, every time I called the writeToSpreadSheet, a new file is getting created, and the existing data is getting overwritten. I want one file only, and
need new worksheeets to be added to my existing file. How do I achieve this?
I am not near my machine so I can't provide you the exact code base, but if you follow the exact steps then you could achieve the desired results.
I have assembled the code from here and there and its not going to work as is. You have to modify the code and made it work the way you wanted. I leave that part for you.
final File file = "/output/test.xls";
HSSFWorkbook workbook = null;
FileOutputStream fileOut = new FileOutputStream(file);
private static void writeToSpreadSheet(String test,
Map<String, String> errorMap, Object object) throws IOException {
// Check whether your file exist
// if not then crate a workbook
// something like below
if (!file.exists()) {
System.out.println("Creating a new workbook '" + file + "'");
workbook = new HSSFWorkbook();
} else {
// create a method to get very last sheet number something like
// following .
int sheetIndex = getLastSheetIndex();
// if you dont to go with find last sheet index idea then you can
// create your unique name may be like timestamp or so
// add the new sheet with new index
HSSFSheet sheet = workbook.createSheet("Test Sheet " + sheetIndex);
// Write your content
workbook.write(fileOut);
fileOut.close();
}
}
private static int getLastSheetIndex() {
int sheetIndex = 1;
while (workbook.getSheet("Test Sheet " + sheetIndex) != null) {
sheetIndex++;
}
return sheetIndex;
}
Currently what are you doing:
private static void writeToSpreadSheet(String test,
Map<String, String> errorMap, Object object) throws IOException {
// Creating the new workbook every time you call this method
HSSFWorkbook workbook = new HSSFWorkbook();
// Adding the same sheet to newly created workbook
HSSFSheet sheet = workbook.createSheet(test);
FileOutputStream fis = new FileOutputStream("output/test.xls");
// causing to overwrite your old workbook
workbook.write(fis);
if (fis != null)
fis.close();
}

Categories