Is it possible to write in cell like POI using POITransformer? - java

This is what I need to do.
1) Accept an xlsx/xls file from client.
2) Backend will receive it in the form of multipart file
3) The file will be processed and if the format of the data is invalid, that same file will be updated and the error message will be written in the side of the input of the client.
4) this modified file will be sent back to the user.
But after several attempts, I could not make my code work.
def generateErrorReport(ServletResponse response, Map messageCollections, MultipartFile file, String ext){
FileInputStream fileIn = file.getInputStream()
Workbook workbook = (ext.equalsIgnoreCase("xls")) ? new HSSFWorkbook(fileIn) : new XSSFWorkbook(fileIn)
workbook = this.getWorkbook((MultipartFile) file, ext.equalsIgnoreCase("xls"));
try {
Sheet sheet = workbook.getSheetAt(0)
Long lastCellNum = sheet.getRow(0).getLastCellNum();
for(int i=1; i<sheet.getLastRowNum(); i++){
if(messageCollections[i]!=null && messageCollections[i]!=[] ) {
Cell cell = sheet.getRow(i).getCell(lastCellNum + 1)
cell.setCellValue(messageCollections[i]);
}
}
fileIn.close()
FileOutputStream fileOut = new FileOutputStream((File) file)
workbook.write(fileOut);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
response.setHeader("Content-Disposition", "Attachment;Filename=error.xlsx")
response.outputStream << fileOut
response.outputStream.flush()
fileOut.close()
}catch(Exception ex){
println ex
}
}
Now, I think a found way to do it, that is to use POITransformer. The problem is it is used when you have a template. My idea was to use the file sent by the client as template and then just simply write the error message beside the client's input. However I couldnt find a way to write it because I couldn't find a setCellData or any method same as that.
The question is, is it possible to write to a desired cell data using POITransformer? If yes, how will I do that?
So far this is what i've done. But it still not writing. Could you tell me whats wrong?
private void bla(ServletResponse response, Map messageCollections, MultipartFile file, String ext){
InputStream is = file.getInputStream();
OutputStream os = response.outputStream;
String fileName = "error";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "Attachment;Filename=${fileName}");
try {
PoiTransformer transformer = PoiTransformer.createTransformer(is, os);
org.apache.poi.ss.usermodel.Workbook workbook = transformer.getWorkbook()
Sheet sheet = workbook.getSheetAt(workbook.getActiveSheetIndex())
int lastColNum = sheet.getRow(0).getLastCellNum()
Cell cellData;
(0..sheet.getLastRowNum()) {
if (messageCollections[it]!=null && messageCollections[it]!=[]) {
cellData = sheet.getRow(it).getCell(lastColNum+1);
cellData.setCellValue(messageCollections[it].toString())
}
}
transformer.write();
} catch (IOException ex) {
println ex
// Logger.getLogger(ExcelFileHandler.class.getName()).log(Level.SEVERE, null, ex);
} finally {
closeStream(is);
closeStream(os);
}
}

Hopefully, this answer would be helpful to others.
InputStream is = file.getInputStream();
OutputStream os = response.outputStream;
String fileName = "desiredFilename." + ext
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "Attachment;Filename=${fileName}");
try {
PoiTransformer transformer = PoiTransformer.createTransformer(is, os);
org.apache.poi.ss.usermodel.Workbook workbook = transformer.getWorkbook()
Sheet sheet = workbook.getSheetAt(workbook.getActiveSheetIndex())
int lastColNum = sheet.getRow(0).getLastCellNum()
Cell cell;
cell = sheet.getRow(0).getCell(lastColNum);
if(cell==null){
cell = sheet.getRow(0).createCell(lastColNum);
}
cell.setCellType(1)
cell.setCellValue("Message")
cell.setCellStyle(getStyle(workbook, 2))
for(int it=1; it<sheet.getLastRowNum(); it++) {
if (message.get(new Long(it))!=null && message.get(new Long(it))!=[]) {
cell = sheet.getRow(it).getCell(lastColNum);
if(cell==null){
cell = sheet.getRow(it).createCell(lastColNum);
}
cell.setCellType(1)
cell.setCellValue(message.get(new Long(it)).join(', '))
cell.setCellStyle(getStyle(workbook, 1))
}
}
sheet.autoSizeColumn(lastColNum);
transformer.write();

Related

File update followed by file upload in Selenium with java

I have a scenario where first I update an excel column with certain values using one test case (test case 1) and then use that file for upload in the next test case (test case 2). I am able to successfully update the file and also able to browse the file for upload. the problem is that the content is not being read from the file. I just have to open the excel file created and perform the save action manually and then when I run the test (test case 2)related to uploading it works perfectly fine. I am not sure what is causing the issue. it would be of great help if someone can support this issue.
Here are the test steps
Update file column values - code snippet
public void setColValues(String fileName, String sheetName, int colIndex, List<Integer> sData) {
try {
String excelPath = System.getProperty("director to file path");
FileInputStream fis = new FileInputStream(excelPath);
XSSFWorkbook workbook = new XSSFWorkbook(fis);
XSSFSheet sh = workbook.getSheet(sheetName);
int rowCount = sh.getLastRowNum();
logger.info(rowCount);
int index = 0;
for (int rowCounter = 2; rowCounter <= rowCount; rowCounter++) {
sh.getRow(rowCounter).getCell(colIndex).setCellValue(sData.get(index));
index++;
}
fis.close();
FileOutputStream fos = new FileOutputStream(new File(excelPath), false);
workbook.write(fos);
workbook.close();
fos.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
go to a specific URL
Click the browse button and pass the file path
Click the button to upload
It might be because you are not passing a File instance to FileInputStream constructor?
You wrote:
FileInputStream fis = new FileInputStream(excelPath);
Try instead:
FileInputStream inputStream = new FileInputStream(new File(excelPath));

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

Is it possible to write an Excel file using usermodel and read the same file again using eventmodel in apache poi

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

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.

Categories