Write items into excel cell - java

I am absolute new to the Eclipse Java coding. I am trying to finish a project for managing the inventory. The part i am having trouble with is that, when I tried to write the items into the excel cell, I got errors saying that the array is out of bounds.
PS: item and item.getPartname etc are all defined under another class file.
Please help. thanks
FileOutputStream os =new FileOutputStream("orderreceipt");
//Create a new workbook for writing data
HSSFWorkbook wb2 = new HSSFWorkbook();
//Create a new sheet:
HSSFSheet newsheet = wb2.createSheet("MyNewSheet");
//Create a new row:
for (int i=0; i<6; i++){
HSSFRow newrow = newsheet.createRow(i);
sentorder item = (sentorder)items.get(i);
for (short j=0; j<5; j++){
HSSFCell cell = newrow.createCell(j);
cell.setCellValue(item.getPartname());
cell.setCellValue(item.getPartnumber());
cell.setCellValue(item.getQuantity());
cell.setCellValue(new Date());
HSSFCellStyle styleOfCell = wb2.createCellStyle();
styleOfCell.setDataFormat(HSSFDataFormat
.getBuiltinFormat("m/d/yy"));
styleOfCell.setFillForegroundColor(HSSFColor.AQUA.index);
styleOfCell.setFillPattern(HSSFCellStyle.BORDER_THIN);
cell.setCellStyle(styleOfCell);
}}
wb2.write(os);
}

I can see quite a few problems with the attached code:
Missing file extension when creating new FileOutputStream - since you're generating the .xls workbook, you'd probably like to store it in the XLS file (the extension is not added automatically), also just make sure you have a proper file path to the directory you have write permissions (local application dir, as in this case should be ok though).
As already mentioned you are re-setting the same cell value 4 times
You are creating the same cell style multiple times (this is not cached behind the scenes and there is largely limited number of cells styles which can be created so if you were generating a couple thousands of rows you might get into troubles
You don't flush() and close() stream after writing your workbook. Streams in Java a precious resources which need to be manually closed.
Without stack trace it's difficult to say 100% where the ArrayOutOfBound issue you're seeing is coming from, however my guess would be that you're trying to access a item (from items collection) with the index that doesn't exist, which is a consequence that you're driving your report data from row indexes instead of the list of items you have.
Also, since you're quite new to Java a couple of guidelines which will allow you to produce hopefully better and less error-prone code in the future:
Use proper Java naming convention - please follow standard Java naming convention http://java.about.com/od/javasyntax/a/nameconventions.htm , your code will be easier to read and reason about (especially when you're looking for help from community) - i.e. sentorder class should be named as SentOrder.
Try to split your code into smaller, more testable modules i.e. you can have a helper createDataRow method called from your main method, in general having more than a couple of inner loops in one method makes them incredibly difficult to test, debug and reason about.
Unless you really need to generate .xls format, consider using XSSF* classes for generating xlsx document - it has many improvements over HSSF* (including much better dataFormat support).
Having those in mind I've rewritten your example:
public void improved(List<SentOrder> items) throws IOException {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("MyNewSheet");
HSSFCellStyle styleOfCell = workbook.createCellStyle();
styleOfCell.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy"));
styleOfCell.setFillForegroundColor(HSSFColor.AQUA.index);
styleOfCell.setFillPattern(HSSFCellStyle.BORDER_THIN);
int rowIndex = 0;
for(SentOrder item : items) {
HSSFRow row = sheet.createRow(rowIndex++);
HSSFCell nameCell = row.createCell(0);
nameCell.setCellValue(item.getPartName());
HSSFCell numberCell = row.createCell(1);
numberCell.setCellValue(item.getPartNumber());
HSSFCell quantityCell = row.createCell(2);
quantityCell.setCellValue(item.getQuantity());
HSSFCell dateCell = row.createCell(3);
dateCell.setCellValue(new Date());
dateCell.setCellStyle(styleOfCell);
}
FileOutputStream os = new FileOutputStream("order_receipt.xls");
try {
workbook.write(os);
} finally {
os.flush();
os.close();
}
}

Related

Java Out of memory error for reading from and writing to an xlsx

I need to read several xlsx files looking for data specific to an employee and simultaneously create another xlsx file (if I find data in any of the file)with file name as employee Id appended to the name I found the data in. Eg. there is an employee with emp id 1 and there are severaal xlsx files such as A,B, C... so on; I need to look for data relating to emp id 1 in each file and for the files I get a hit I need to create a file named 1_A.xlsx.
Now although I have built the logic and am using Apache POI APIs for reading and writing, my code is throwing Out Of Memory error after creating just the first file with the data. And is unable to read the rest of the files.
I have tried using SXSSF instead of XSSF but same OOM happens.
Increasing the heap space is not an option for me.
Please help here...Thanks in advance.
Here is a piece of code :
//Reader:
Row row = null;
List<Row> listOfRecords = new ArrayList<Row>();
try {
FileInputStream fis = new FileInputStream(metaDataFile);
new InputStreamReader(fis, "ISO-8859-1");
XSSFWorkbook wb = new XSSFWorkbook(fis);
XSSFSheet sheet = wb.getSheetAt(0);
Iterator<Row> rowIterator = sheet.iterator();
while (rowIterator.hasNext()) {
row = rowIterator.next();
if (!isEmptyRow(row)) {
listOfRecords.add(row);
}
}
wb.close();
fis.close();
//Writer
LOGGER.info("in createWorkbook " );
Workbook empWorkbook = new SXSSFWorkbook(200);
Sheet empSheet = empWorkbook.createSheet("Itype Sheet For Emp_"
+ personnelNumber);
int rowNum = listOfRecords.size();
System.out.println("Creating excel");
Cell c = null;
for (int i = 0; i < rowNum; i++) {
Row record = listOfRecords.get(i);
Row empRow = empSheet.createRow(i++);
if (!isEmptyRow(record)) {
int colNum = record.getLastCellNum() + 1;
for (int j = 0; j < colNum; j++) {
Cell newCell = empRow.createCell(j);
System.out.println("cellVal:"
+ String.valueOf(record.getCell(j)));
newCell.setCellValue(String.valueOf(record.getCell(j)));
}
}
}
The writer method is called from within the reader.
Reading of multiple xlsx files is indeed tricky business butI finally solved it.
I had to break down my code several folds to realise that the OOM error was due to the fact that after reading 3 files no more memory was left to process the rest of the files.
xlsx files are compressed xml files. So when we try to read them using XSSF or SXSSF APIs it loads the entire DOM to the memory thereafter choking it.
I found an excellent solution here :
[https://github.com/monitorjbl/excel-streaming-reader]
Hope this will help others who come here facing the same issue.

HWPF-POI:The table insert into doc with poi hwpf is not visible

I want to insert a table at a specific position with poi, the table is generated, but I find this table is not visible.
The generated table in doc is visible when previewing or editing this doc with macOS and its text tool, POI can read the table and content, too. I plan to upload 4 pictures to display the process, but I can only post 2 images, sorry for that.
#Test
public void exportDoc() throws Exception {
FileInputStream readFile = new FileInputStream(new File(readDoc));
FileOutputStream replaceFile = new FileOutputStream(new File(replaceDoc));
HWPFDocument document = new HWPFDocument(readFile);
Table table = WordUtil.insertNewTable(document,"${table}");
insertTableInDoc(table);
document.write(replaceFile);
readFile.close();
replaceFile.close();
}
private Table insertNewTable(HWPFDocument doc, String sourceValue) {
Range range = doc.getRange();
Table table = null;
for (int i = 0; i < range.numSections(); ++i) {
Section s = range.getSection(i);
for (int x = 0; x < s.numParagraphs(); x++) {
Paragraph p = s.getParagraph(x);
if (p.text().contains(sourceValue)) {
//remove target text
range.replaceText(sourceValue, "");
table = p.insertTableBefore((short) 3, 3);
return table;
}
}
}
return table;
}
private void insertTableInDoc(Table table) {
int count = 1;
for (int rowNum = 0; rowNum < table.numRows(); rowNum++) {
TableRow tableRow = table.getRow(rowNum);
for (int colNum = 0; colNum < tableRow.numCells(); colNum++) {
TableCell cell = tableRow.getCell(colNum);
Paragraph paragraph = cell.getParagraph(0);
CharacterRun characterRun = paragraph.getCharacterRun(0);
characterRun.insertBefore("number: " + count++);
}
}
}
PS:
I am sure this is not microsoft for mac 's problem, the generate table in doc at windows platform is not visible, too.
(First time to ask question, if anything wrong or my expression is not clear, please let me know and I will modify it without delay. Thanks)
With the current state of the HWPF project, you likely are out of luck when trying to insert content into a .doc file. Your best bet is to use a different format (docx).
I did not look at HWPF for the past year, so I may be wrong here regarding the current state of HWPF:
Some years ago I was developing a custom HWPF library for a client. The major goal for that custom library was the ability to modify .doc files and that Word can process the modified files correctly. Hence I know in how many levels modifying a .doc file can fail in the end. The public HWPF library is not able to handle many aspects of the .doc file format when it comes to modification (textboxes, two-byte character ranges, shape files, nested tables, ... to name a few).
To handle modification correctly, all the "features" of the specific .doc file must be supported by the library. So when there are shapes in the .doc file, HWPF must adjust the position tables of the shapes even when a simple text snippet is inserted and the shapes are not touched. If shapes are not handled, Word will crash when opening the output file.
So if you can, use docx or rtf. If it is an option, you might try one of the commercial libraries which are able to handle .doc files.

Apache Poi, how to get the linked workbook?

I try to analyze excel files with links to other files and I like to know the file name and path. For that I'm using apache poi 3.14.
I figured it out for Ref3DPtg objects but for Ref3DPxg I don't know how to do it. I only get access to the cell address and the sheet name.
Does anyone know how to do it?
Code:
...
if(ptg instanceof Ref3DPxg){
cellAddress = ptg.format2DRefAsString();
sheetName = ptg.getSheetName();
workbookName = ???;
} else if(ptg instanceof Ref3DPtg) {
// by Ref3DPtg is no problem
}
Because of the way that the XLSX file format stores external references, which isn't actually =[Other.xlsx]Sheet1!A1 but actually =[23]Sheet1!A1, it's a two step process. First, get the external workbook number from the Pxg. Next, from Workbook get the ExternalLinks table for that workbook number, noting the off-by-one. (External Workbook 0 is actually the current workbook, so External Workbook 1 corresponds to External Link 0). Finally, fetch the filename for that link
So, your code should be something like:
if(ptg instanceof Ref3DPxg){
Ref3DPxg pxg = (Ref3DPxg)ptg;
int extWB = pxg.getExternalWorkbookNumber();
int extLink = extWB-1;
ExternalLinksTable links = wb.getExternalLinksTable().get(extLink);
String filename = links.getLinkedFileName();
}

Why am I getting the number of rows in an Excel sheet as 0 using JAVA?

I am trying to write some data into an Excel file with format (.xlsx). But the thing is that the first row of it is written into the file correctly and when I am trying to retrieve the number of rows and then increment the rows so that I could write more rows into the file, I am getting an exception of "Invalid format exception" and sometimes my code is getting executed but I am getting row count as 1. Please solve my issue and I am even attaching the code am working with.
public static int getRows(String memail,String testid) throws FileNotFoundException,IOException, InvalidFormatException{
int rc;
FileInputStream file = new FileInputStream(new File("email1.xlsx"));
XSSFWorkbook wb = new XSSFWorkbook (file);
WorkbookFactory.create(file);
XSSFSheet sheet = wb.getSheet(testid);
rc = sheet.getLastRowNum();
return rc;
}
Remove the following line
WorkbookFactory.create(file);
and try again.
If you have already a created file, while you are creating that again...
You can try Sheet.getPhysicalNumberOfRows(), this will returns the number of physically defined rows (NOT the number of rows in the sheet).One more point are you sure you are getting the correct sheet from wb.getSheet(testid);?

Excel sheet POI validation: Out Of Memory Error

I am trying to validate an Excel file using Java before dumping it to database.
Here is my code snippet which causes error.
try {
fis = new FileInputStream(file);
wb = new XSSFWorkbook(fis);
XSSFSheet sh = wb.getSheet("Sheet1");
for(int i = 0 ; i < 44 ; i++){
XSSFCell a1 = sh.getRow(1).getCell(i);
printXSSFCellType(a1);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Here is the error I get
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.ArrayList.<init>(Unknown Source)
at java.util.ArrayList.<init>(Unknown Source)
at org.apache.xmlbeans.impl.values.NamespaceContext$NamespaceContextStack.<init>(NamespaceContext.java:78)
at org.apache.xmlbeans.impl.values.NamespaceContext$NamespaceContextStack.<init>(NamespaceContext.java:75)
at org.apache.xmlbeans.impl.values.NamespaceContext.getNamespaceContextStack(NamespaceContext.java:98)
at org.apache.xmlbeans.impl.values.NamespaceContext.push(NamespaceContext.java:106)
at org.apache.xmlbeans.impl.values.XmlObjectBase.check_dated(XmlObjectBase.java:1273)
at org.apache.xmlbeans.impl.values.XmlObjectBase.stringValue(XmlObjectBase.java:1484)
at org.apache.xmlbeans.impl.values.XmlObjectBase.getStringValue(XmlObjectBase.java:1492)
at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellImpl.getR(Unknown Source)
at org.apache.poi.xssf.usermodel.XSSFCell.<init>(XSSFCell.java:105)
at org.apache.poi.xssf.usermodel.XSSFRow.<init>(XSSFRow.java:70)
at org.apache.poi.xssf.usermodel.XSSFSheet.initRows(XSSFSheet.java:179)
at org.apache.poi.xssf.usermodel.XSSFSheet.read(XSSFSheet.java:143)
at org.apache.poi.xssf.usermodel.XSSFSheet.onDocumentRead(XSSFSheet.java:130)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.onDocumentRead(XSSFWorkbook.java:286)
at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:159)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:207)
at com.xls.validate.ExcelValidator.main(ExcelValidator.java:79)
This works perfectly fine when the .xlsx file is less than 1 MB.
I understand this is because my .xlsx file is around 5-10 MB and POI tries to load the entire sheet at once in JVM memory.
What can be a possible workaround?
There are two options available to you. Option #1 - increase the size of your JVM Heap, so that Java has more memory available to it. Processing Excel files in POI using the UserModel code is DOM based, so the whole file (including parsed form) needs to be buffered into memory. Try a question like this one for advice on how to increase the help.
Option #2, which is more work - switch to event based (SAX) processing. This only processes part of the file at a time, so needs much much less memory. However, it requires more work from you, which is why you might be better throwing a few more GB of memory at the problem - memory is cheap while programmers aren't! The SpreadSheet howto page has instructions on how to do SAX parsing of .xlsx files, and there are various example files provided by POI you can look at for advice.
.
Also, another thing - you seem to be loading a File via a stream, which is bad as it means even more stuff needs buffering into memory. See the POI Documentation for more on this, including instructions on how to work with the File directly.
You can use SXSSF workbook from POI for memory related issues. Refer here
I faced the similar issue while reading and merging multiple CSVs into a single XLSX file.
I had a total of 3 csv sheets each with 30k rows totalling to 90k.
It got resolved by using SXSFF as below,
public static void mergeCSVsToXLSX(Long jobExecutionId, Map<String, String> csvSheetNameAndFile, String xlsxFile) {
try (SXSSFWorkbook wb = new SXSSFWorkbook(100);) { // keep 100 rows in memory, exceeding rows will be flushed to
// disk
csvSheetNameAndFile.forEach((sheetName, csv) -> {
try (CSVReader reader = new CSVReader(new FileReader(csv))) {
wb.setCompressTempFiles(true);
SXSSFSheet sheet = wb.createSheet(sheetName);
sheet.setRandomAccessWindowSize(100);
String[] nextLine;
int r = 0;
while ((nextLine = reader.readNext()) != null) {
Row row = sheet.createRow((short) r++);
for (int i = 0; i < nextLine.length; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(nextLine[i]);
}
}
} catch (IOException ioException) {
logger.error("Error in reading CSV file {} for jobId {} with exception {}", csv, jobExecutionId,
ioException.getMessage());
}
});
FileOutputStream out = new FileOutputStream(xlsxFile);
wb.write(out);
wb.dispose();
} catch (IOException ioException) {
logger.error("Error in creating workbook for jobId {} with exception {}", jobExecutionId,
ioException.getMessage());
}
}
Use Event API (HSSF Only).
The event API is newer than the User API. It is intended for intermediate developers who are willing to learn a little bit of the low level API structures. Its relatively simple to use, but requires a basic understanding of the parts of an Excel file (or willingness to learn). The advantage provided is that you can read an XLS with a relatively small memory footprint.
Well, here's a link with some detailed info about your error, and how to fix it: http://javarevisited.blogspot.com/2011/09/javalangoutofmemoryerror-permgen-space.html?m=1.
Well, let me try to explain your error:
The java.lang.OutOfMemoryError has two variants. One in the Java Heap Space, and the other in PermGen Space.
Your error could be caused by a memory leak, a low amount of system RAM, or very little RAM allocated to the Java Virtual Machine.
The difference between the Java Heap Space and PermGen Space variants is that PermGen Space stores pools of Strings and data on the primitive types, such as int, as well as how to read methods and classes, the Java Heap Space works differently. So if you have a lot of strings or classes in your project, and not enough allocated/system RAM, you will get an OutOfMemoryError. The default amount of RAM the JVM allocates to PermGen is 64 MB, which is quite a small bit of memory space. The linked article explains much more about this error and provides detailed information about how to fix this.
Hope this helps!
To resolve Outofmemery error follow this.
You can not modify existing cells in a SXSSFWorkbook but you can create the new file along with your modification using SXSSFWorkbook.
It's possible by passing the workbook object along with rowaccesswindow size.
SXSSFWorkbook workbook = new SXSSFWorkbook( new XSSFWorkbook(new FileInputStream(file)),100);
//Your changes in workbook
workbook.write(out);
To resolve Outofmemery error, follow this.
You can not modify existing cells in a SXSSFWorkbook, but you can create the new file along with your modification using SXSSFWorkbook.
It's possible by passing the workbook object along with rowaccesswindow size.
SXSSFWorkbook workbook = new SXSSFWorkbook( new XSSFWorkbook(new FileInputStream(file)),100);
//Your changes in workbook
workbook.write(out);
I too faced the same issue of OOM while parsing xlsx file...after two days of struggle, I finally found out the below code that was really perfect;
This code is based on sjxlsx. It reads the xlsx and stores in a HSSF sheet.
[code=java]
// read the xlsx file
SimpleXLSXWorkbook = new SimpleXLSXWorkbook(new File("C:/test.xlsx"));
HSSFWorkbook hsfWorkbook = new HSSFWorkbook();
org.apache.poi.ss.usermodel.Sheet hsfSheet = hsfWorkbook.createSheet();
Sheet sheetToRead = workbook.getSheet(0, false);
SheetRowReader reader = sheetToRead.newReader();
Cell[] row;
int rowPos = 0;
while ((row = reader.readRow()) != null) {
org.apache.poi.ss.usermodel.Row hfsRow = hsfSheet.createRow(rowPos);
int cellPos = 0;
for (Cell cell : row) {
if(cell != null){
org.apache.poi.ss.usermodel.Cell hfsCell = hfsRow.createCell(cellPos);
hfsCell.setCellType(org.apache.poi.ss.usermodel.Cell.CELL_TYPE_STRING);
hfsCell.setCellValue(cell.getValue());
}
cellPos++;
}
rowPos++;
}
return hsfSheet;[/code]

Categories