I'm getting an error when trying to open an Excel sheet with MS office 2003. This Excel sheet is created using HSSFWorkbook, implementing the usermodel scope org.apache.poi.hssf.usermodel
In Microsoft Excel 2003: "Too many different cell formats". In Microsoft Excel 2007/2010, files may produce the following error message: "Excel found unreadable content in the file". This is regarding cell formats, please refer to the below page:
http://support.microsoft.com/kb/213904
So how can I fix this issue in code?
Excel has a limit on the number of different cells styles you can have, and it's surprisingly low. A common problem for people new to working with POI is that they skip over the bit about cell styles being workbook wide, and instead they create one cell style per cell. This quickly pushes them over the limit in Excel...
Where you code might previously have looked something like
Sheet s = wb.createSheet();
for (int rn=0; rn<=10; rn++) {
Row r = s.createRow(rn);
for (int cn=0; cn<=4; cn++) {
Cell c = r.createCell(c);
c.setCellValue( getMyCellValue(rn,cn) );
CellStyle cs = wb.createCellStyle();
cs.setBold(true);
if (cn == 2) {
cs.setDataFormat( DataFormat.getFormat(yyyy/mm/dd) );
}
c.setCellStyle(cs);
}
}
You instead need to pull your cell style creation out to the start, something like
CellStyle bold = wb.createCellStyle();
bold.setBold(true);
CellStyle boldDate = wb.createCellStyle();
boldDate.setBold(true);
boldDate.setDataFormat( DataFormat.getFormat(yyyy/mm/dd) );
Sheet s = wb.createSheet();
for (int rn=0; rn<=10; rn++) {
Row r = s.createRow(rn);
for (int cn=0; cn<=4; cn++) {
Cell c = r.createCell(c);
c.setCellValue( getMyCellValue(rn,cn) );
CellStyle cs = bold;
if (cn == 2) {
cs = boldDate;
}
c.setCellStyle(cs);
}
}
Related
I'm trying to generate excel file with 200k records. But it is taking almost 2 hours to generate the file.
Here is my code of generating excel file.
Workbook workbook=null;
csvFileName = userId+"_Records_"+new SimpleDateFormat("yyyyMMddHHmmss")
.format(new Date())+".xls";
path = ReadPropertyFile.getProperties("download.reports.path");
misService.insertXLSRecord(ackNo,"-",null, VspCommonConstants.getIpFromRequest(request),
new Date(), userId,"N",userReportRoleId);
workbook = getWorkbook(path+csvFileName);
Sheet sheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(studAppForm.get(0)
.getScheme_Id()+"_"+studAppForm.get(0).getEFP_Scholarship_Name(),'_'));
if(schemeQuestionData.containsKey(currSheetSchemeId))
createXLSHeaders(sheet,schemeQuestionData.get(currSheetSchemeId));
Row row = sheet.createRow(++rowCount);
currAppId=studAppForm.get(j).getApp_Id().toString();
jspTableAppIds.remove(jspTableAppIds.indexOf(new BigInteger(currAppId)));
writeBook(studAppForm.get(j), row);
Here is my createXLSHeaders method to create header
void createXLSHeaders( Sheet sheet, List<SchemeMasterBean> schemeMasterBeanList){
LOGGER.info("Creating XLS SheetHeaders for sheet "+sheet.getSheetName());
// Sheet sheet = workbook.createSheet();
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("APPLICATION ID");
header.createCell(1).setCellValue("APPLICATION STATUS");
header.createCell(2).setCellValue("APPLICATION DATE");
header.createCell(3).setCellValue("SCHEME/SCHOLARSHIP APPLIED");
header.createCell(4).setCellValue("SCHEME ID");
header.createCell(5).setCellValue("STUDENT ID");
header.createCell(6).setCellValue("STUDENT FULL NAME");
.
.
.
62 heading...
int i=73;
if(schemeMasterBeanList!=null)
for(SchemeMasterBean schemeMasterBean :schemeMasterBeanList){
if(!schemeMasterBean.getSmSchemeType().equals("5") &&
!schemeMasterBean.getSmSchemeType().equals("6")){
header.createCell(i).setCellValue(schemeMasterBean.getSmScholarshipName());
i++;
}
}
}
and finally writebook method
private void writeBook(StudentAppFormVsp saf, Row row) throws JSONException {
Cell cell = row.createCell(0);
cell.setCellValue(saf.getApp_Id()!=null?saf.getApp_Id().toString():"");
cell = row.createCell(1);
cell.setCellValue(saf.getApp_Status()!=null?getApplicationStatusMap().get(saf.getApp_Status()):"");
cell = row.createCell(2);
cell.setCellValue(saf.getCrtn_time()!=null?saf.getCrtn_time().toString():"");
cell = row.createCell(3);
cell.setCellValue(saf.getEFP_Scholarship_Name()!=null?saf.getEFP_Scholarship_Name().toString():"");
cell = row.createCell(4);
cell.setCellValue(saf.getScheme_Id()!=null?saf.getScheme_Id().toString():"");
cell = row.createCell(5);
cell.setCellValue(saf.getStud_Id()!=null?saf.getStud_Id().toString():"");
.
.
62 rows
}
How to reduce the excel sheet generation time?
First: play around with memory for the application if possible.
Then: the tip on using a profiler is really worth the effort.
Any DOM, XML, Excel or otherwise often suffer from location references searching from top to the actual position.
Creating a DOM instead of writing sequentially is costly with respect to memory, and can slow things down. Maybe consider this.
You could make two loop: writing to a CSV file, and then creating an XLS(X).
Then you know where the complexity resides.
The following (I rewrote a bit) is slightly suspect: toString + new BigInteger points to a conversion; I hope not from BigInteger to String to BigInteger.
StudentAppFormVsp saf = studAppForm.get(j);
currAppId = saf.getApp_Id().toString();
jspTableAppIds.remove(jspTableAppIds.indexOf(BigInteger.valueOf(currAppId)));
writeBook(saf, row);
Is there any particular reason POI creates a numeric cell when in fact, it's actually empty? What happens is that by the time I get to that cell I obviously get an runtime error as I can't get the string value (it's a numeric cell, after all) and I can't get the numeric value either (can't get a number from an empty string) so my 2 questions would be:
How does POI got there?
Is there any way I can handle this scenario without having to explicitly go to my excel file?
UPDATE I:
After reading / parsing my excel file, POI generates the following XML for this particular cell:
<x:c r="AA2" t="n">
<x:v/>
</x:c>
My method is something like this:
final FileInputStream inputStream = new FileInputStream(new File("PATH/TO/FILE.xml"));
final XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
final XSSFSheet sheet = workbook.getSheet("SHEET_NAME");
final int columnCount = sheet.getRow(0).getLastCellNum();
for (int rowNumber = 0; rowNumber <= sheet.getLastRowNum(); rowNumber++)
{
final XSSFRow row = sheet.getRow(rowNumber);
for (int column = 0; column < columnCount; column++)
{
// By now my cell would throw an exception if I attempt cell.getStringCellValue() or cell.getNumericCellValue() as cell.getCellType() returns "0" / NUMERIC
final XSSFCell cell = row.getCell(column);
...
}
}
I was thinking about adding an additional validation to determine whether the cell is empty by using the getRawValue() method, but not sure if there's a better way to handle this as it was obviously wrongly parsed.
UPDATE II
I've been able to reproduce this scenario by doing adding this as part of a unit test (I still don't understand why POI would fall into this scenario though):
final XLSXSheetConverterImpl xlsxSheetConverter = new XLSXSheetConverterImpl();
xlsxSheetConverter.setSheetName(SHEET_NAME);
xlsxSheetConverter.setFilePrefix(FILE_PREFIX);
XSSFWorkbook workbook = new XSSFWorkbook();
final XSSFSheet sheet = workbook.createSheet(SHEET_NAME);
final XSSFRow row = sheet.createRow(0);
XSSFCell cell = row.createCell(0);
final CTCellImpl ctCell = (CTCellImpl) cell.getCTCell();
ctCell.setT(STCellType.N);
ctCell.setV("");
Thanks in advance!
This is a known bug in older POI versions, see: https://bz.apache.org/bugzilla/show_bug.cgi?id=56702
It's been fixed since 3.11 version
Thank you all for your help!!
I am using Apache POI to create an excel file. I am using short hand notation to create cells and I want to know if there is a way to fill the colors on the cells using the same coding pattern.
Coding standard 1.
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue(NYPG3Constants.FIRST_NAME);
headerRow.createCell(1).setCellValue(NYPG3Constants.LAST_NAME);
headerRow.createCell(2).setCellValue(NYPG3Constants.POLICY_NUMBER);
headerRow.createCell(3).setCellValue(NYPG3Constants.ZIP_CODE);
headerRow.createCell(4).setCellValue(NYPG3Constants.DATE_OF_BIRTH);
I can set styles using following coding pattern but for every cell headers I need to create a separate cell object.
Coding standard 2
CellStyle style = workBook.createCellStyle();
style.setFillForegroundColor(HSSFColor.GOLD.index);
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
Cell cell = headerRow.createCell(0);
cell.setCellValue(NYPG3Constants.FIRST_NAME);
cell.setCellStyle(style);
Is there a way to fill colors on the header cells using my first coding standard?
Thanks in advance
What about a simple for loop?
CellStyle style = workBook.createCellStyle();
style.setFillForegroundColor(HSSFColor.GOLD.index);
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
...
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue(NYPG3Constants.FIRST_NAME);
headerRow.createCell(1).setCellValue(NYPG3Constants.LAST_NAME);
headerRow.createCell(2).setCellValue(NYPG3Constants.POLICY_NUMBER);
headerRow.createCell(3).setCellValue(NYPG3Constants.ZIP_CODE);
headerRow.createCell(4).setCellValue(NYPG3Constants.DATE_OF_BIRTH);
for (int c = 0; c < 5; c++) {
headerRow.getCell(c).setCellStyle(style);
}
I'm using Apache POI to export data to a .xlsx file and I want to style some of the rows and cells contained in the file.
I'm using XSSF since the file is going to be read in Excel 2007+.
Basically, my problem is that I'm trying to set a row style like in the following example, which sets a black foreground color for the entire row at index 0. It works fine, but whenever I create a new cell, the newly created cell has no style, as if it's overriding the row style I specified.
Here's a code snippet to demonstrate what I'm doing:
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet("mySheet");
XSSFRow row = sheet.createRow(0);
XSSFCellStyle myStyle = wb.createCellStyle();
myStyle.setFillForegroundColor(new XSSFColor(new Color(255, 255, 255)));
myStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
row.setRowStyle(myStyle); //This works, the whole row is now black
row.createCell(0); // This cell doesn't have a style, the rest of the line stays stylized
row.getCell(0).setCellValue("Test");
I also tried *row.createCell(0, Cell.CELL_TYPE_STRING);*, but it didn't change anything.
What is the correct way of accomplishing what I want to do? I wanted to do it this way so I didn't have to set each cell's style after creating it since all cells on the same row have the same style.
Set the style into newly created cell as well e.g. below:
XSSFCell newCell = row.createCell(0);
newCell.setCellStyle(myStyle);
Even you create a row with style, it will not effect to created cell of its. The create cell have their own cell style.
The row style will not override to cell style automatically. If you would like use row style in cell, you have to set again.
Even if you set row style at end, it will not effect to cell.
Example
CreationHelper createHelper = wb.getCreationHelper();
Sheet sheet = wb.createSheet("new sheet");
Row r = sheet.createRow(0);
r.setRowStyle(rowStyle);
Cell c1 = r.createCell(0);
c1.setCellValue("Test 1");
c1.setCellStyle(rowStyle);
I'm agree that "setRowStyle" doesn't work as it should be.
I created my own function to apply a style to a range ( could be a row or multiple row )
public void applyStyleToRange(Sheet sheet, CellStyle style, int rowStart, int colStart, int rowEnd, int colEnd) {
for (int r = rowStart; r <= rowEnd; r++) {
for (int c = colStart; c <= colEnd; c++) {
Row row = sheet.getRow(r);
if (row != null) {
Cell cell = row.getCell(c);
if (cell != null) {
cell.setCellStyle(style);
}
}
}
}
}
I am using Apache POI API to generate excel spreadsheet to output some data.
The problem I am facing is when the spreadsheet is created and opened, columns are not expanded so that some long text like Date formatted text is not showing up on first glance.
I could just double click the column border in excel to expand or drag the border to adjust the column width but there could be 20+ columns and there is no way I want to do that manually every time I open the spreadsheet :(
I found out (though could be wrong method) groupRow() and setColumnGroupCollapsed() might be able to do the trick but no luck. Maybe I'm using it in wrong way.
Sample Code snippet
Workbook wb = new HSSFWorkbook();
CreationHelper createHelper = wb.getCreationHelper();
//create sheet
Sheet sheet = wb.createSheet("masatoSheet");
//not really working yet.... :(
//set group for expand/collapse
//sheet.groupRow(0, 10); //just random fromRow toRow argument values...
//sheet.setColumnGroupCollapsed(0, true);
//create row
Row row = sheet.createRow((short)0);
//put a cell in the row and store long text data
row.createCell(0).setCellValue("Loooooooong text not to show up first");
When this spreadsheet is created, the "Looooooong text not to show up first" string is in the cell but since the column is not expanded only "Loooooooo" is showing up.
How can I configure it so that when I open my spreadsheet, the column is already expanded???
After you have added all your data to the sheet, you can call autoSizeColumn(int column) on your sheet to autofit the columns to the proper size
Here is a link to the API.
See this post for more reference
Problem in fitting the excel cell size to the size of the content when using apache poi
Tip : To make Auto size work , the call to sheet.autoSizeColumn(columnNumber) should be made after populating the data into the excel.
Calling the method before populating the data, will have no effect.
If you want to auto size all columns in a workbook, here is a method that might be useful:
public void autoSizeColumns(Workbook workbook) {
int numberOfSheets = workbook.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
Sheet sheet = workbook.getSheetAt(i);
if (sheet.getPhysicalNumberOfRows() > 0) {
Row row = sheet.getRow(sheet.getFirstRowNum());
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
int columnIndex = cell.getColumnIndex();
sheet.autoSizeColumn(columnIndex);
}
}
}
}
You can try something like this:
HSSFSheet summarySheet = wb.createSheet();
summarySheet.setColumnWidth(short column, short width);
Here params are:column number in sheet and its width
But,the units of width are pretty small, you can try 4000 for example.
For Excel POI:
sheetName.autoSizeColumn(cellnum);
sample code below
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("your sheet name");
HSSFRow row = sheet.createRow(0);
cell = row.createCell(0);
cell.setCellValue("A BIG NAME WITH AUTO SIZE FEATURE ENABLED");
//this is crucial
sheet.autoSizeColumn(0);
//argument must be cell number
cell = row.createCell(1);
cell.setCellValue("a big name without auto size feature enabled");
Check the output and go nuts :)
If you know the count of your columns (f.e. it's equal to a collection list). You can simply use this one liner to adjust all columns of one sheet (if you use at least java 8):
IntStream.range(0, columnCount).forEach(sheet::autoSizeColumn)
You can add this, after your loop.
for (int i = 0; i<53;i++) {
sheet.autoSizeColumn(i);
}
I use below simple solution:
This is your workbook and sheet:
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("YOUR Workshhet");
then add data to your sheet with columns and rows. Once done with adding data to sheet write following code to autoSizeColumn width.
for (int columnIndex = 0; columnIndex < 15; columnIndex++) {
sheet.autoSizeColumn(columnIndex);
}
Here, instead 15, you add the number of columns in your sheet.
Hope someone helps this.
You can use setColumnWidth() if you want to expand your cell more.
Its very simple, use this one line code
dataSheet.autoSizeColumn(0)
or give the number of column in bracket
dataSheet.autoSizeColumn(cell number )
You can wrap the text as well. PFB sample code:
CellStyle wrapCellStyle = new_workbook.createCellStyle();
wrapCellStyle.setWrapText(true);