I am writing some data into excel file using Apache POI but for some reason the file shows only the last record (1 record only). I have list of POLJO that I am passing. I am also iterating through the cells but all I get is just one record.
Method to write in excel
public void writeToExcel(List<NYProgramTO> to){
try {
Workbook workBook = new HSSFWorkbook();
CreationHelper helper = workBook.getCreationHelper();
Sheet sheet = workBook.createSheet("NY_PPA_P3_Sheet");
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("First Name");
headerRow.createCell(1).setCellValue("Last Name");
headerRow.createCell(2).setCellValue("Policy Number");
headerRow.createCell(3).setCellValue("Zip Code");
headerRow.createCell(4).setCellValue("Date of Birth");
if(to != null){
int size = to.size();
for(int i = 0; i < size; i++){
NYProgramTO nyP= to.get(i);
Row row = sheet.createRow(1);
row.createCell(0).setCellValue(nyP.getFirstName());
row.createCell(1).setCellValue(nyP.getLastName());
row.createCell(2).setCellValue(nyP.getPolicyNumber());
row.createCell(3).setCellValue(nyP.getZipCode());
row.createCell(4).setCellValue(nyP.getDateOfBirth());
}
}
FileOutputStream stream = new FileOutputStream("NY_PPA_P3.xlsx");
workBook.write(stream);
stream.close();
System.out.println("NY_PPA_P3.xlsx created successfully.");
} catch (Exception ex) {
ex.printStackTrace();
}
}
If by "only one record" you mean that only one row is appearing, this is probably easily fixable by making sure that you increment the Row that is being created before writing the Cells.
Try changing:
Row row = sheet.createRow(1);
to:
Row row = sheet.createRow(i+1);
Related
I am iterating through a list of data which I am sending from the runner file(FunctionVerifier.java).
When I am calling the function writeExcel() in excelHandler.java it is entering only the last data from the list that I am iterating through.
Can someone please let me know the reason and how to fix this
public void writeExcel(String sheetName, int r, int c, String data) throws IOException {
file = new FileInputStream(new File(inFilePath));
wb = new XSSFWorkbook(file);
Sheet sh;
sh = wb.getSheet(sheetName);
Row row = sh.createRow(r);
row.createCell(c).setCellValue(data);
closeExcelInstance();
FileOutputStream outputStream = new FileOutputStream(inFilePath);
wb.write(outputStream);
wb.close();
outputStream.close();
}
public void closeExcelInstance() {
try {
file.close();
} catch (Exception e) {
System.out.println(e);
}
}
FunctionVerifier.java
package Sterling.oms;
import Sterling.oms.Process.CouponValidationProcess;
import Sterling.oms.Utilities.ExcelHandler;
import java.util.ArrayList;
public class FuncVerify {
public static void main(String args[]) throws Exception {
String filePath = System.getProperty("user.dir") + "/src/test/resources/TestData/test.xlsx";
ExcelHandler excelHandler = new ExcelHandler(filePath);
excelHandler.readExcelData("Validation");
CouponValidationProcess couponValidationProcess = new CouponValidationProcess("OMS-T781");
excelHandler.createSheet();
// couponValidationProcess.enterValidationHeaderRowInExcel(filePath);
String sheet = "ValidationData";
if (excelHandler.getRowCountWhenNull(sheet) < 1) {
ArrayList<String> header = new ArrayList<>();
header.add("Test Case");
header.add("Coupon ID");
header.add("Grand Total");
header.add("Manual Grand Total");
for (int i = 0; i < header.size(); i++) {
// excelHandler = new ExcelHandler(filePath);
excelHandler.writeExcel(sheet, 0, i, header.get(i));
// excelHandler.closeExcelInstance();
}
}
}
}
The reason for only storing the last item is that Sheet.createRow as well as Row.createCell are doing exactly what their method names tell. They create a new empty row or cell each time they get called. So every times Row row = sh.createRow(r) gets called, it creates a new empty row at row index r and looses all former created cells in that row.
The correct way to use rows would be first trying to get the row from the sheet. And only if it is not present (null), then create a new row. The same is for cells in rows. First try to get them. And only if not present, then create them.
...
Sheet sh;
sh = wb.getSheet(sheetName);
Row row = sh.getRow(r); if (row == null) row = sh.createRow(r);
Cell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
cell.setCellValue(data);
...
That's the answer to your current question.
But the whole approach, to open the Excel file to create a Workbook, then set data of only one cell in and then write the whole workbook out to the file, and doing this for each cell, is very sub optimal. Instead the workbook should be opened, then all known new cell values should be set into the sheet and then the workbook should be written out.
Your approach is wrong, you open your files again for each line you want to write to Excel, then save again. You just have to create one FileInputStream and send it to your Workbook where you do all your Excel work. After you have finished writing all your lines, you can create only one FileOutputStream and export your changes in your Workbook to a file of your choice.
writeExcel()
public void writeExcel(String sheetName, int r, int c, ArrayList<String> data) throws IOException {
file = new FileInputStream(new File(inFilePath));
wb = new XSSFWorkbook(file);
Sheet sh;
sh = wb.getSheet(sheetName);
Row row = sh.createRow(r);
//Adding data column wise
for (String h : data) {
row.createCell(c++).setCellValue(h);
}
closeExcelInstance();
FileOutputStream outputStream = new FileOutputStream(inFilePath);
wb.write(outputStream);
wb.close();
outputStream.close();
}
i am trying to get all column records in excel using apache poi. But i am getting only last column data in excel.
Below is my code. In below code i am trying to create new row when it reaches specific number of columns. and in each row it as to put all column data.
public static ByteArrayInputStream export(List<String> records) {
int TOTAL_COLUMN = 8;
ByteArrayInputStream output = null;
XSSFWorkbook workbook = null;
InputStream inputStream = null;
try (ByteArrayOutputStream excelFileStream = new ByteArrayOutputStream();) {
inputStream =
new ClassPathResource(TEMPLATE_PATH).getInputStream();
workbook = new XSSFWorkbook(inputStream);
XSSFSheet sheet = workbook.getSheetAt(DATA_SHEET_INDEX);
XSSFCellStyle evenRowCellStyle = createCellStyle(workbook, EVEN_ROW_CELL_COLOR);
XSSFCellStyle oddRowCellStyle = createCellStyle(workbook, ODD_ROW_CELL_COLOR);
Integer rowIndex = STARTING_ROW;
int numberOfColumn = 0;
XSSFCellStyle cellStyle = oddRowCellStyle;
/**
* Populates row cell details with list data
*/
int totalColumn = TOTAL_COLUMN;
for (String data : records) {
if (numberOfColumn == totalColumn) {
rowIndex++;
numberOfColumn = 0;
}
cellStyle = (rowIndex % 2 == 0) ? evenRowCellStyle : oddRowCellStyle;
CellUtil.createCell(sheet.createRow(rowIndex), numberOfColumn, data, cellStyle);
numberOfColumn++;
}
workbook.write(excelFileStream);
output = new ByteArrayInputStream(excelFileStream.toByteArray());
} catch (Exception e) {
output = null;
log.info("Error occurred while exporting to excel sheet.");
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
log.error("Error occurred while closing excel.");
}
}
Utils.closeInputStream(inputStream);
}
return output;
}
above code is giving only last column data in each row.
In your code the call sheet.createRow(rowIndex) always creates a new empty row. So all formerly set cell values in that row get lost.
You are using CellUtil already. There is CellUtil.getRow what does the following:
Get a row from the spreadsheet, and create it if it doesn't exist.
This not always creates a new empty row. Instead it tries to get the row at first and only creates a new row if the row does not exists already,
So do using:
CellUtil.createCell(CellUtil.getRow(rowIndex, sheet), numberOfColumn, data, cellStyle);
I am reading excel file using POI library in my java code. So far fine. But now I have one requirement. The excel file contains many records (e.g. 1000 rows). It also has column headers (1st row). Now I am doing excel filtering on it. Say I have one 'year' column and I am filtering all rows for year=2019. I get 15 rows.
Question: I want to process only these 15 rows in my java code. Is there any method in poi library or way to know if the row being read is filtered or (the other way i.e. not filtered).
Thanks.
I already have working code but right now I am looking for how to read only filtered row. Nothing new tried yet other than searching in library and forums.
The below code is inside a method. I am not used to formatting with stackoverflow so kindly ignore any formatting issue.
// For storing data into CSV files
StringBuffer data = new StringBuffer();
try {
SimpleDateFormat dtFormat = new SimpleDateFormat(CommonConstants.YYYY_MM_DD); // "yyyy-MM-dd"
String doubleQuotes = "\"";
FileOutputStream fos = new FileOutputStream(outputFile);
// Get the workbook object for XLSX file
XSSFWorkbook wBook = new XSSFWorkbook(new FileInputStream(inputFile));
wBook.setMissingCellPolicy(Row.RETURN_BLANK_AS_NULL);
// Get first sheet from the workbook
//XSSFSheet sheet = wBook.getSheetAt(0);
XSSFSheet sheet = wBook.getSheet(CommonConstants.METADATA_WORKSHEET);
//Row row;
//Cell cell;
// Iterate through each rows from first sheet
int rows = sheet.getLastRowNum();
int totalRows = 0;
int colTitelNumber = 0;
Row firstRowRecord = sheet.getRow(1);
for (int cn = 0; cn < firstRowRecord.getLastCellNum(); cn++) {
Cell cellObj = firstRowRecord.getCell(cn);
if(cellObj != null) {
String str = cellObj.toString();
if(CommonConstants.COLUMN_TITEL.equalsIgnoreCase(str)) {
colTitelNumber = cn;
break;
}
}
}
// Start with row Number 1. We don't need 0th number row as it is for Humans to read but not required for processing.
for (int rowNumber = 1; rowNumber <= rows; rowNumber++) {
StringBuffer rowData = new StringBuffer();
boolean skipRow = false;
Row rowRecord = sheet.getRow(rowNumber);
if (rowRecord == null) {
LOG.error("Empty/Null record found");
} else {
for (int cn = 0; cn < rowRecord.getLastCellNum(); cn++) {
Cell cellObj = rowRecord.getCell(cn);
if(cellObj == null) {
if(cn == colTitelNumber) {
skipRow = true;
break; // The first column cell value is empty/null. Which means Titel column cell doesn't have value so don't add this row in csv.
}
rowData.append(CommonConstants.CSV_SEPARTOR);
continue;
}
switch (cellObj.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
rowData.append(cellObj.getBooleanCellValue() + CommonConstants.CSV_SEPARTOR);
//LOG.error("Boolean:" + cellObj.getBooleanCellValue());
break;
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cellObj)) {
Date date = cellObj.getDateCellValue();
rowData.append(dtFormat.format(date).toString() + CommonConstants.CSV_SEPARTOR);
//LOG.error("Date:" + cellObj.getDateCellValue());
} else {
rowData.append(cellObj.getNumericCellValue() + CommonConstants.CSV_SEPARTOR);
//LOG.error("Numeric:" + cellObj.getNumericCellValue());
}
break;
case Cell.CELL_TYPE_STRING:
String cellValue = cellObj.getStringCellValue();
// If string contains double quotes then replace it with pair of double quotes.
cellValue = cellValue.replaceAll(doubleQuotes, doubleQuotes + doubleQuotes);
// If string contains comma then surround the string with double quotes.
rowData.append(doubleQuotes + cellValue + doubleQuotes + CommonConstants.CSV_SEPARTOR);
//LOG.error("String:" + cellObj.getStringCellValue());
break;
case Cell.CELL_TYPE_BLANK:
rowData.append("" + CommonConstants.CSV_SEPARTOR);
//LOG.error("Blank:" + cellObj.toString());
break;
default:
rowData.append(cellObj + CommonConstants.CSV_SEPARTOR);
}
}
if(!skipRow) {
rowData.append("\r\n");
data.append(rowData); // Appending one entire row to main data string buffer.
totalRows++;
}
}
}
pTransferObj.put(CommonConstants.TOTAL_ROWS, (totalRows));
fos.write(data.toString().getBytes());
fos.close();
wBook.close();
} catch (Exception ex) {
LOG.error("Exception Caught while generating CSV file", ex);
}
All rows which are not visible in the sheet have a zero height. So if the need is only reading the visible rows, one could check via Row.getZeroHeight.
Example
Sheet:
Code:
import java.io.FileInputStream;
import org.apache.poi.ss.usermodel.*;
class ReadExcelOnlyVisibleRows {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("SAMPLE.xlsx"));
DataFormatter dataFormatter = new DataFormatter();
CreationHelper creationHelper = workbook.getCreationHelper();
FormulaEvaluator formulaEvaluator = creationHelper.createFormulaEvaluator();
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
if (!row.getZeroHeight()) { // if row.getZeroHeight() is true then this row is not visible
for (Cell cell : row) {
String cellContent = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.print(cellContent + "\t");
}
System.out.println();
}
}
workbook.close();
}
}
Result:
F1 F2 F3 F4
V2 2 2-Mai FALSE
V4 4 4-Mai FALSE
V2 6 6-Mai FALSE
V4 8 8-Mai FALSE
You have to use auto filter provided in Apache Poi library and also you have set the freezing. I provide below the brief code snippet, you can use accordingly.
XSSFSheet sheet = wBook.getSheet(CommonConstants.METADATA_WORKSHEET);
sheet.setAutoFilter(new CellRangeAddress(0, 0, 0, numColumns));
sheet.createFreezePane(0, 1);
I had to override some hooks and come up with my own approach to incorporate filtering of hidden rows in order to prevent processing of those. Below is code snippet. My approach consists of opening a second copy of the same sheet just so that I can query the current row getting processed to see if it's hidden or not. The answer above touches on this, the below expands on it to show how it can be nicely incorporated into the Spring batch excel framework. One drawback is that you have to open a second copy of the same file, but I couldn't figure out a way (perhaps there's none!) to get my hands on the internal Workbook sheet, among other reasons because org.springframework.batch.item.excel.poi.PoiSheet is package private (Note that below syntax is Groovy!!!):
/**
* Produces a reader that knows how to ingest a file in excel format.
*/
private PoiItemReader<String[]> createExcelReader(String filePath) {
File f = new File(filePath)
PoiItemReader<String[]> reader = new PoiItemReader<>()
reader.setRowMapper(new PassThroughRowMapper())
Resource resource = new DefaultResourceLoader().getResource("file:" + f.canonicalPath)
reader.setResource(resource)
reader.setRowSetFactory(new VisibleRowsOnlyRowSetFactory(resource))
reader.open(new ExecutionContext())
reader
}
...
// The "hooks" I overwrote to inject my logic
static class VisibleRowsOnlyRowSet extends DefaultRowSet {
Workbook workbook
Sheet sheet
VisibleRowsOnlyRowSet(final Sheet sheet, final RowSetMetaData metaData) {
super(sheet, metaData)
}
VisibleRowsOnlyRowSet(final Sheet sheet, final RowSetMetaData metaData, Workbook workbook) {
this(sheet, metaData)
this.workbook = workbook
this.sheet = sheet
}
boolean next() {
boolean moreLeft = super.next()
if (moreLeft) {
Row row = workbook.getSheet(sheet.name).getRow(getCurrentRowIndex())
if (row?.getZeroHeight()) {
log.warn("Row $currentRow is hidden in input excel sheet, will omit it from output.")
currentRow.eachWithIndex { _, int i ->
currentRow[i] = ''
}
}
}
moreLeft
}
}
static class VisibleRowsOnlyRowSetFactory extends DefaultRowSetFactory {
Workbook workbook
VisibleRowsOnlyRowSetFactory(Resource resource) {
this.workbook = WorkbookFactory.create(resource.inputStream)
}
RowSet create(Sheet sheet) {
new VisibleRowsOnlyRowSet(sheet, super.create(sheet).metaData, workbook)
}
}
Hi i am new to writing data to excel however after doing much research and reading documentation I have done it.
My Problem:
When string data is stored in my array and written it to excel however it does not put it in one column (which is what I want) it spaces each string out in a column to the right of it in a diagonal descent in my spread sheet. A picture is below.
What I want is for each string/name to be put in one column under each other. Any suggestions would be appreciated.
Code Snippet:
System.out.println("Write data to an Excel Sheet");
FileOutputStream out = new FileOutputStream("C:/Users/hoflerj/Desktop/text2excel/finish.xlsx");
XSSFWorkbook workBook = new XSSFWorkbook();
XSSFSheet spreadSheet = workBook.createSheet("clientid");
XSSFRow row;
XSSFCell cell;
for(int i=0;i<arr.size();i++){
row = spreadSheet.createRow((short) i);
cell = row.createCell(i);
System.out.println(arr.get(i));
cell.setCellValue(arr.get(i).toString());
}
System.out.println("Done");
workBook.write(out);
arr.clear();
out.close();
You are increasing column index as well.
Just change from:
cell = row.createCell(i);
To:
cell = row.createCell(0);
So the For Loop will be like:
for(int i=0;i<arr.size();i++){
row = spreadSheet.createRow((short) i);
cell = row.createCell(0);
System.out.println(arr.get(i));
cell.setCellValue(arr.get(i).toString());
}
Or with fewer lines:
for(int i=0;i<arr.size();i++){
row = spreadSheet.createRow((short) i);
row.createCell(0).setCellValue( arr.get(i).toString() );
System.out.println(arr.get(i));
}
I'm using apache poi to create an excel document. To create new sheet in workbook I write next code:
Workbook wb = new HSSFWorkbook();
Sheet sh = wb.createSheet();
this code create and add sheet to workbook. But I want to create sheet formerly and then add it to workbook. Smth like this:
Sheet sh = new HSSFSheet();
wb.addSheet(sh);
I need such thing, because I want to copy data from one sheet of one workbook to another sheet of another workbook(Workbook interface has method Sheet cloneSheet(int)). But Workbook interface doesn't have method like addSheet(Sheet sh).
Also HSSFWorkbook is final class so I can't extend it to implement add method
How can I do this?
You can't just take a Sheet object from one Workbook, and add it to a different Workbook.
What you'll need to do is to open the old workbook and the new workbooks at the same time, and create the sheet in the new workbook. Next, clone all the styles you used in the old sheet onto the new one (HSSFCellStyle has a method for cloning a style from one workbook to another). Finally, iterate over all the cells and copy them over.
You should use RangeCopier.
XSSFWorkbook workbookFrom = new XSSFWorkbook(new File("/path/to/workbookFrom.xlsx"));
XSSFSheet sheetFrom = workbookFrom.getSheetAt(0);
XSSFWorkbook workbookTo = new XSSFWorkbook(new File("/path/to/workbookTo.xlsx"));
XSSFSheet sheetTo = workbookTo.createSheet("sheet1");
workbookTo.setSheetOrder("sheet1", 0);
XSSFRangeCopier xssfRangeCopier = new XSSFRangeCopier(sheetFrom, sheetTo);
int lastRow = sheetFrom.getLastRowNum();
int lastCol = 0;
for (int i = 0; i < lastRow; i++) {
Row row = sheetFrom.getRow(i);
if (row != null) {
if (row.getLastCellNum() > lastCol) {
lastCol = row.getLastCellNum();
}
sheetTo.setDefaultRowHeight(sheetFrom.getDefaultRowHeight());
}
}
for (int j = 0; j < lastCol; j++) {
sheetTo.setColumnWidth(j, sheetFrom.getColumnWidth(j));
}
CellRangeAddress cellAddresses = new CellRangeAddress(0, lastRow, 0, lastCol);
xssfRangeCopier.copyRange(cellAddresses, cellAddresses, true, true);
workbookTo.write(new FileOutputStream(new File("/path/to/worksheetTo.xlsx")));
POI version < v4.0
Okay I tried to do what Gagravarr said above. This solution works for me. This code will work if the sheets don't have tables, etc. If the sheets contain simple text (String, boolean, int etc), formulas, this solution will work.
Workbook oldWB = new XSSFWorkbook(new FileInputStream("C:\\input.xlsx"));
Workbook newWB = new XSSFWorkbook();
CellStyle newStyle = newWB.createCellStyle(); // Need this to copy over styles from old sheet to new sheet. Next step will be processed below
Row row;
Cell cell;
for (int i = 0; i < oldWB.getNumberOfSheets(); i++) {
XSSFSheet sheetFromOldWB = (XSSFSheet) oldWB.getSheetAt(i);
XSSFSheet sheetForNewWB = (XSSFSheet) newWB.createSheet(sheetFromOldWB.getSheetName());
for (int rowIndex = 0; rowIndex < sheetFromOldWB.getPhysicalNumberOfRows(); rowIndex++) {
row = sheetForNewWB.createRow(rowIndex); //create row in this new sheet
for (int colIndex = 0; colIndex < sheetFromOldWB.getRow(rowIndex).getPhysicalNumberOfCells(); colIndex++) {
cell = row.createCell(colIndex); //create cell in this row of this new sheet
Cell c = sheetFromOldWB.getRow(rowIndex).getCell(colIndex, Row.CREATE_NULL_AS_BLANK ); //get cell from old/original WB's sheet and when cell is null, return it as blank cells. And Blank cell will be returned as Blank cells. That will not change.
if (c.getCellType() == Cell.CELL_TYPE_BLANK){
System.out.println("This is BLANK " + ((XSSFCell) c).getReference());
}
else { //Below is where all the copying is happening. First It copies the styles of each cell and then it copies the content.
CellStyle origStyle = c.getCellStyle();
newStyle.cloneStyleFrom(origStyle);
cell.setCellStyle(newStyle);
switch (c.getCellTypeEnum()) {
case STRING:
cell.setCellValue(c.getRichStringCellValue().getString());
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
cell.setCellValue(c.getDateCellValue());
} else {
cell.setCellValue(c.getNumericCellValue());
}
break;
case BOOLEAN:
cell.setCellValue(c.getBooleanCellValue());
break;
case FORMULA:
cell.setCellValue(c.getCellFormula());
break;
case BLANK:
cell.setCellValue("who");
break;
default:
System.out.println();
}
}
}
}
}
//Write over to the new file
FileOutputStream fileOut = new FileOutputStream("C:\\output.xlsx");
newWB.write(fileOut);
oldWB.close();
newWB.close();
fileOut.close();
If your requirement is to copy full sheets without leaving or adding anything. I think The process of elimination works better and faster then the above code. And you don't have to worry about losing formulas, drawings, tables, styles, fonts, etc.
XSSFWorkbook wb = new XSSFWorkbook("C:\\abc.xlsx");
for (int i = wb.getNumberOfSheets() - 1; i >= 0; i--) {
if (!wb.getSheetName(i).contentEquals("January")) //This is a place holder. You will insert your logic here to get the sheets that you want.
wb.removeSheetAt(i); //Just remove the sheets that don't match your criteria in the if statement above
}
FileOutputStream out = new FileOutputStream(new File("C:\\xyz.xlsx"));
wb.write(out);
out.close();
POI version >= v4.0
As of version 4.0, Cell.CELL_TYPE_BLANK and Row.CREATE_NULL_AS_BLANK don't exist (they deprecated). Use CellType.* and Row.MissingCellPolicy.* instead.