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();
}
Related
Hello All
Hey,it's omkaar as a software tester(Automation Selenium+Java).
There is a scinario of reading excel file using Automation (selenium+java)which having .xls (Microsoft Excel 97-2003 Worksheet) file extension.
Now i am not able to read excel file due to the error/popup massege(Please have look attched screenshot) which is obsereved while opening that file manually.
Steps which i have followed to read excel file.
Created object of file class and give the refference of that perticular file.
Created workbook instance.
Created sheet, row and cell instance.
Used "for each loop" to iterate through each cell to get the data.
(You can also go through attached code.)
here is my code.
public class WorkWithXLSFile extends BaseTest {
public static void main(String[] args) {
try {
File file = new File("C:\\Users\\Omkar Shrotri\\Downloads\\Ticketdetails.xls");
if (file.exists()) {
FileInputStream fis = new FileInputStream(file);
HSSFWorkbook wb = new HSSFWorkbook(fis);
HSSFSheet sheet1 = wb.getSheetAt(0);
DataFormatter formatter11 = new DataFormatter();
for (int i = 0; i <= 7; i++) {
String excel_data = formatter11.formatCellValue(sheet1.getRow(2).getCell(i));
// int s = i+1;
String web_data = driver.findElement(By.xpath("//table/tbody/tr[1]/td['" + i + "']")).getText();
if (excel_data.equals(web_data)) {
System.out.println("Passed");
}
}
}
} catch (Exception NotOLE2FileException) {
System.out.print(NotOLE2FileException);
// handle popup
// try code
}
}
}
I need to create a HSSFWorkbook or an XSSFWorkbook obect, depending on what i read as file extension and then be able to proceed operations and stuff with the object created. How can i make the Object visibile outside the IF statement, so that i can use it "globally"?
I've tried with a method, but we know that a method can only return ONE object type and i am dealing with 2 possible object types output (HSSF/XSSF Workbook)
String excelFilePath = "D://"; //path
String fileName = "BetsTable"; //filename
String extension = "xls"; //extension
String completePath = excelFilePath + fileName + "." + extension; //fullpath
FileInputStream inputStream = new FileInputStream(new
File(completePath));
if(extension == "xls") {
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
}
if(extension == "xlsx") {
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
}
Sheet firstSheet = workbook.getSheetAt(0); // !!! WORKBOOK IS NOW NOT
"USABLE"
I expect nothing, since i know how the scope works in this case, but i need a way to fulfill this option
In short, no.
But, you can keep a Sheet outside the inner scope, you just need to define it in the scope you need it:
Sheet sheet = null;
if(extension == "xls") {
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
sheet = workbook.getSheetAt(0);
}
if(extension == "xlsx") {
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
sheet = workbook.getSheetAt(0);
}
// sheet is accessible from here
doSomething(sheet);
In general you shouldn't use == for String comparisons. Use equals.
But concerning your problem: It is not possible. When you create the variable within a block it is not accessible outside the block.
In terms of clean code it might be good practice to create an own method for it. Then you also don't have the problem any more.
//your code starts here
String excelFilePath = "D://"; //path
String fileName = "BetsTable"; //filename
String extension = "xls"; //extension
String completePath = excelFilePath + fileName + "." + extension; //fullpath
FileInputStream inputStream = new FileInputStream(new File(completePath));
Sheet firstSheet = getFirstSheet(inputStream, extension);
//your code ends here (don't forget to check if firstSheet is null)
private static Sheet getFirstSheet(FileInputStream inputStream, String extension) throws IOException {
if("xls".equals(extension)) {
return new HSSFWorkbook(inputStream).getSheetAt(0);
} else if("xlsx".equals(extension)) {
return new XSSFWorkbook(inputStream).getSheetAt(0);
} else {
return null;
}
}
It's possible if both variables shares a common type, and if you don't expect to use a method which is only in one of those classes.
In your case, both classes implements the interface Workbook and that's where the method getSheetAt(int) is defined.
Workbook workbook = null;
if(extension == "xls") { // that's bad, use equals
workbook = new HSSFWorkbook(inputStream);
}
if(extension == "xlsx") { // that's bad, use equals
workbook = new XSSFWorkbook(inputStream);
}
Sheet firstSheet = workbook.getSheetAt(0);
Warning: This code will throw a NPE if the extension is neither "xls" or "xlsx"
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?
I tried writing to a new Excel file using the following code (uses usermodel)
private static void writeToExecelFileUsingUserModel() throws InvalidFormatException, IOException {
String[] header = {"","A","B","C", "D","E","F","G","I","J"};
String[] dataSet = {"1","2","3","4","5","6","7","8","9","10"};
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = (HSSFSheet) workbook.createSheet("testSheet");
HSSFRow row = sheet.createRow(0);
for(int i= 0; i <header.length; i++ ){
HSSFCell cell = row.createCell(i);
cell.setCellValue(header[i]);
}
HSSFRow row2 = sheet.createRow(1);
for(int i= 0; i <dataSet.length; i++ ){
HSSFCell cell = row2.createCell(i);
cell.setCellValue(dataSet[i]);
}
try {
FileOutputStream fos = new FileOutputStream("C:\\Test.xls");
workbook.write(fos);
System.out.println("write complete");
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Then, I used the same file and tried reading using eventmodel using the code below. It gave the error:
Exception in thread "main"
org.apache.poi.openxml4j.exceptions.InvalidFormatException: Package
should contain a content type part [M1.13] at
org.apache.poi.openxml4j.opc.ZipPackage.getPartsImpl(ZipPackage.java:199)
at
org.apache.poi.openxml4j.opc.OPCPackage.getParts(OPCPackage.java:665)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:274)
at
com.benz.test.ReadFromExcel.readUsingEventModel(ReadFromExcel.java:34)
at com.benz.test.ReadFromExcel.main(ReadFromExcel.java:24)
//code for reading from previously generated xls file using eventmodel
private static void readUsingEventModel() throws IOException, OpenXML4JException {
InputStream excelStream = null;
OPCPackage pkg = null;
System.out.println("reading using event model");
try {
FileInputStream myxls = new FileInputStream("C:\\Test.xls");
pkg = OPCPackage.open(myxls);
XSSFReader xssfReader = new XSSFReader(pkg);
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
String sheetName = iter.getSheetName();
System.out.println("sheet name is"+sheetName);
} finally {
if (excelStream != null) {
excelStream.close();
}
if (pkg != null) {
pkg.close();
}
}
}
Also the same scenario (ie writing using usermodel and reading using event model) works fine for xlsx file but does not work for xls files.
Also I cannot use usermodel for reading as it is giving performance issues
Any help would be greatly appreciated.Thanks
Your first set of code is all HSSF, which only works for .xls files:
HSSFWorkbook workbook = new HSSFWorkbook();
Then, a little later, you're suddenly trying to use the XSSF code which only works for .xlsx files:
OPCPackage pkg = null;
XSSFReader xssfReader = new XSSFReader(pkg);
You have two choices. Firstly, you can change your initial code to be XSSF, using XSSFWorkbook and friends. If you generate your Excel file with XSSF as a .xlsx, then you can read it with XSSF code. Alternately, if you really want to be using HSSF / .xls for generation, and you want to use low-memory reading, then you need to use the HSSF Event API to do your read
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.