아파치 POI로 하는 엑셀 프로그래밍-2

2022. 9. 7. 12:13프로그래밍

728x90

 

Apache POI 라이브러리를 이용해서 엑셀 파일을 만드는 작업을 한 번 해 보았습니다.

 

아파치 POI로 하는 Excel 프로그래밍

마이크로 소프트에서 나온 엑셀 파일을 직접 프로그램으로 작성해서 만들어 볼 수 있는 것으로... https://blog.naver.com/tommybee/222056850634 [Excel] apache poi를 사용한 엑셀파일 만들기 어제 만들어 본 sw..

tobee.tistory.com

해 볼 것이 여러가지 있을 텐데요. 워낙 잘 만들어진 라이브러라 여러 곳에서 레퍼런스들을 많이 발견 할 수 있을 것입니다. 여기서 한 가지 엑셀을 만들었다면, 이렇게 만들어진 엑셀 파일인 workbook 여러 개를 합쳐서 만들어 보는 작업을 한 번 해 보려고 합니다.

  • 엑셀 파일-Workbook- 합치기

위 파일 합치는 것은 찾아 보면 많이 나와 있습니다.

저의 경우에는 다음 스택 오버플로우 답변을 사용 했습니다.

일단 저번에 만들어 봤던 메서드들이 다 똑 같기 때문에, 해당 하는 메서드 말고 Workbook 객체를 반환하는 메서드들을 하나씩 만들어 보겠습니다.

public static Workbook getWorkbookForMyDataExcel(List<MyExcelObj> myexcelObjList)
{
	Workbook workbook = null;
	Sheet sheet = null;
	
	final String sheetName = "MyFirstSheet";
	
	final String headerNames[][] 
		= new String[][]{
		{"컬럼1",	"컬럼2",	"컬럼3",	"컬럼4",	"컬럼5",	"컬럼6",	"컬럼7"},
		{"Col1", "Col2", "Col3", "Col4", "Col5", "Col6", "Col7"}
	};
	
	final int numberOfColumns = headerNames[0].length;
	//logger.debug("numberOfColumns:{}", numberOfColumns);
	try
	{
		workbook = new XSSFWorkbook();
		sheet = workbook.createSheet(sheetName);
		
		Row headerRow = sheet.createRow(0);
		
		String columnName = null;
		// exclude the first column which is the ID field
		for (int i = 0; i < numberOfColumns; i++) {
			columnName = headerNames[0][i];
			Cell headerCell = headerRow.createCell(i);
			headerCell.setCellValue(columnName);
			columnName = null;
		}

	} catch (Exception e) {
		System.out.println("File IO error:");
		e.printStackTrace();
	}
	
	String Col1 = null;
	String Col2 = null;
	String Col3 = null;
	String Col4 = null;
	String Col5 = null;
	String Col6 = null;
	String Col7 = null;

	MyExcelObj selfObj = null;
	
	try
	{
		int rowCount = 1;
		
		for(Iterator<MyExcelObj> checkIter = myexcelObjList.iterator(); checkIter.hasNext();)
		{
			selfObj = checkIter.next();
			Row row = sheet.createRow(rowCount++);
			
			Col1 = selfObj.getCol1();
			Col2 = selfObj.getCol2();
			Col3 = selfObj.getCol3();
			Col4 = selfObj.getCol4();
			Col5 = selfObj.getCol5();
			Col6 = selfObj.getCol6();
			Col7 = selfObj.getCol7();
			
			
			Cell cell1 = row.createCell(0);
			Cell cell2 = row.createCell(1);
			Cell cell3 = row.createCell(2);
			Cell cell4 = row.createCell(3);
			Cell cell5 = row.createCell(4);
			Cell cell6 = row.createCell(5);
			Cell cell7 = row.createCell(6);
			
			cell1.setCellValue(Col1);
			cell2.setCellValue(Col2);
			cell3.setCellValue(Col3);
			cell4.setCellValue(Col4);
			cell5.setCellValue(Col5);
			cell6.setCellValue(Col6);
			cell7.setCellValue(Col7);
			
			
			Col1 = null;
			Col2 = null;
			Col3 = null;
			Col4 = null;
			Col5 = null;
			Col6 = null;
			Col7 = null;
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	
	
	return workbook;
}
 
 

그럼, 원래 만들어 놓았던 export 메서드들도 간단히 다음과 같이 변경 하면 되겠죠?

여기서 중요한 점은 sheetName 이름이 중복이 있으면 안된다는 것을 명심 해야 합니다~~

public static void exportMyDataToExcel(List<MyExcelObj> myexcelObjList)
{
	Workbook workbook = null;
	
	try
	{
		workbook = getWorkbookForMyDataExcel(myexcelObjList);
	}
	finally
	{
		try {
			if(workbook != null) workbook.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
 

그리고, 위의 스택오버플로우 답변에 있던 메서드를 Workbook의 리스트를 받는 메서드로 변경 하고, 일반적인 클래스 명으로 다 바꾸어 보겠습니다...

우선, Workbook 객체의 리스트를 받는 메서드를 정의 해 줍니다.

public void exportWorkbookListToExcel(final String fileName, final List<Workbook> workbookList)
{
	Workbook myWorkBook = new XSSFWorkbook();
	int workbooks = workbookList.size();
	Workbook workbook = null;
	
	for (int iWorkbook = 0; iWorkbook < workbooks; iWorkbook++) {
		try {
            workbook = workbookList.get(iWorkbook);
			exportWorkbookListToExcel(myWorkBook, workbook);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	BufferedOutputStream bos;
	try {
		bos = new BufferedOutputStream(
				new FileOutputStream(fileName, true));
		myWorkBook.write(bos);
		
		bos.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
}
 

최종적으로 결과물인 엑셀로 저장 할 객체는 코드에서 보듯이 myWorkBook 객체이며, 실제 구현은 XSSFWorkbook 클래스의 인스턴스 입니다.

그런다음, 메서드를 오버로딩 하여 결과 엑셀 객체와 각 각의 workbook 객체를 받을 수 있는 메서드를 다음과 같이 만들어 주었습니다.

public void exportWorkbookListToExcel(Workbook myWorkBook, Workbook workbook) throws IOException {
	Sheet sheet = null;
	Row row = null;
	Cell cell = null;
	Sheet mySheet = null;
	Row myRow = null;
	Cell myCell = null;
	int sheets = workbook.getNumberOfSheets();
	int fCell = 0;
	int lCell = 0;
	int fRow = 0;
	int lRow = 0;
	for (int iSheet = 0; iSheet < sheets; iSheet++) {
		sheet = workbook.getSheetAt(iSheet);
		if (sheet != null) {
			mySheet = myWorkBook.createSheet(sheet.getSheetName());
			fRow = sheet.getFirstRowNum();
			lRow = sheet.getLastRowNum();
			for (int iRow = fRow; iRow <= lRow; iRow++) {
				row = sheet.getRow(iRow);
				myRow = mySheet.createRow(iRow);
				if (row != null) {
					fCell = row.getFirstCellNum();
					lCell = row.getLastCellNum();
					for (int iCell = fCell; iCell < lCell; iCell++) {
						cell = row.getCell(iCell);
						myCell = myRow.createCell(iCell);
						if (cell != null) {
							CellType thisCelltype = cell.getCellType();
							myCell.setCellType(thisCelltype);
							
							if(thisCelltype == CellType.BLANK)
								myCell.setCellValue("");
							else if(thisCelltype == CellType.BOOLEAN)
								myCell.setCellValue(cell.getBooleanCellValue());
							else if(thisCelltype == CellType._NONE)
								myCell.setCellErrorValue(cell.getErrorCellValue());
							else if(thisCelltype == CellType.FORMULA)
								myCell.setCellFormula(cell.getCellFormula());
							else if(thisCelltype == CellType.NUMERIC)
								myCell.setCellValue(cell.getNumericCellValue());
							else if(thisCelltype == CellType.STRING)
								myCell.setCellValue(cell.getStringCellValue());
							else
								myCell.setCellFormula(cell.getCellFormula());
						}
					}
				}
			}
		}
	}
}
 

그럼 결과를 확인 해 볼까요?

우선 테스트 코드는 다음과 같습니다.

final String fileName = "5)Total_workbook.xlsx";
final List<Workbook> workbookList = new ArrayList<Workbook>();
workbookList.add(TobeeExcelSheet.getWorkbookForMyData(myxcelList));
workbookList.add(TobeeExcelSheet.getWorkbookForMyData2(myxcelList2));
workbookList.add(TobeeExcelSheet.getWorkbookForMyData3(myxcelList3));
workbookList.add(TobeeExcelSheet.getWorkbookForMyData4(myxcelList4));

TobeeExcelSheet.exportWorkbookListToExcel(fileName, workbookList);
 

결과는?

음... 불행히도 스타일이 적용이 안된 것을 확인 할 수가 있었습니다.

그래서 스타일이 적용 될 수 있도록 코드를 다음처럼 바꿔 보았습니다.

for (int iCell = fCell; iCell < lCell; iCell++) {
	cell = row.getCell(iCell);
	myCell = myRow.createCell(iCell);
	if (cell != null) {
		CellType thisCelltype = cell.getCellType();
		myCell.setCellType(thisCelltype);
		
		if(thisCelltype == CellType.BLANK)
			myCell.setCellValue("");
		else if(thisCelltype == CellType.BOOLEAN)
			myCell.setCellValue(cell.getBooleanCellValue());
		else if(thisCelltype == CellType._NONE)
			myCell.setCellErrorValue(cell.getErrorCellValue());
		else if(thisCelltype == CellType.FORMULA)
			myCell.setCellFormula(cell.getCellFormula());
		else if(thisCelltype == CellType.NUMERIC)
			myCell.setCellValue(cell.getNumericCellValue());
		else if(thisCelltype == CellType.STRING)
			myCell.setCellValue(cell.getStringCellValue());
		else
			myCell.setCellFormula(cell.getCellFormula());
		
		myCell.setCellStyle(cell.getCellStyle());
	}
}
 

위의 코드에서 바뀐 부분은

myCell.setCellStyle(cell.getCellStyle());
 

이며, 코드 자체는 그럴싸 해 보입니다...만,

오류의 향기는 어쩔수가 없네요...

다음 스택오버플로우 문서를 확인 해 볼 수가 있었습니다.


그런 식의 코딩은 안 됩니다. CellStyle 객체들은 특정한 하나의 workbook의 객체 들입니다.

이 객체들은 꽤 복잡하며, 대부분의 스타일들은 workbook 내에 있죠, 그래서 간단하게 재 사용 할 수가 없습니다. 그래서 이러한 도움이 되는 예외가 발생 한 것이죠~!

 

이렇게 재 사용 하는 것이 아니라, 전체 스타일의 상세를 복사하기 위해서 cloneStyleFrom(CellStyle) 메서드를 대신 사용 할 필요가 있습니다.

아래 코드 처럼요:

Workbook wb = WorkbookFactory.create(new File("existing.xls"));
CellStyle origStyle = wb.getCellStyleAt(1); // Or from a cell

Workbook newWB = new XSSFWorkbook();
Sheet sheet = newWB.createSheet();
Row r1 = sheet.createRow(0);
Cell c1 = r1.createCell(0);

CellStyle newStyle = newWB.createCellStyle();
newStyle.cloneStyleFrom(origStyle);
c1.setCellStyle(newStyle);

newWB.write(new FileOutpuStream("new.xlsx"));
 

위의 코드를 바탕으로 다시 한 번 재 구성 해보도록 하겠습니다.

for (int iCell = fCell; iCell < lCell; iCell++) {
	cell = row.getCell(iCell);
	myCell = myRow.createCell(iCell);
	if (cell != null) {
		CellType thisCelltype = cell.getCellType();
		myCell.setCellType(thisCelltype);
		
		if(thisCelltype == CellType.BLANK)
			myCell.setCellValue("");
		else if(thisCelltype == CellType.BOOLEAN)
			myCell.setCellValue(cell.getBooleanCellValue());
		else if(thisCelltype == CellType._NONE)
			myCell.setCellErrorValue(cell.getErrorCellValue());
		else if(thisCelltype == CellType.FORMULA)
			myCell.setCellFormula(cell.getCellFormula());
		else if(thisCelltype == CellType.NUMERIC)
			myCell.setCellValue(cell.getNumericCellValue());
		else if(thisCelltype == CellType.STRING)
			myCell.setCellValue(cell.getStringCellValue());
		else
			myCell.setCellFormula(cell.getCellFormula());
		
		//myCell.setCellStyle(cell.getCellStyle());
		
		CellStyle newStyle = myWorkBook.createCellStyle();
		newStyle.cloneStyleFrom(cell.getCellStyle());
		myCell.setCellStyle(newStyle);
	}
}
 

다시 결과를 한 번 볼까요?

제대로 스타일이 적용 되어 나오는 것을 확인 해 볼 수 있었습니다.

다음은 exportWorkbookListToExcel 메서드의 최종 본입니다. autoSizeColumn 메서드도 적용 된 거을 확인 할 수 있을 것입니다.

public static void exportWorkbookListToExcel(Workbook myWorkBook, Workbook workbook) throws IOException {
	Sheet sheet = null;
	Row row = null;
	Cell cell = null;
	Sheet mySheet = null;
	Row myRow = null;
	Cell myCell = null;
	int sheets = workbook.getNumberOfSheets();
	int fCell = 0;
	int lCell = 0;
	int fRow = 0;
	int lRow = 0;
	for (int iSheet = 0; iSheet < sheets; iSheet++) {
		sheet = workbook.getSheetAt(iSheet);
		if (sheet != null) {
			mySheet = myWorkBook.createSheet(sheet.getSheetName());
			fRow = sheet.getFirstRowNum();
			lRow = sheet.getLastRowNum();
			for (int iRow = fRow; iRow <= lRow; iRow++) {
				row = sheet.getRow(iRow);
				myRow = mySheet.createRow(iRow);
				if (row != null) {
					fCell = row.getFirstCellNum();
					lCell = row.getLastCellNum();
					for (int iCell = fCell; iCell < lCell; iCell++) {
						cell = row.getCell(iCell);
						myCell = myRow.createCell(iCell);
						if (cell != null) {
							CellType thisCelltype = cell.getCellType();
							myCell.setCellType(thisCelltype);
							
							if(thisCelltype == CellType.BLANK)
								myCell.setCellValue("");
							else if(thisCelltype == CellType.BOOLEAN)
								myCell.setCellValue(cell.getBooleanCellValue());
							else if(thisCelltype == CellType._NONE)
								myCell.setCellErrorValue(cell.getErrorCellValue());
							else if(thisCelltype == CellType.FORMULA)
								myCell.setCellFormula(cell.getCellFormula());
							else if(thisCelltype == CellType.NUMERIC)
								myCell.setCellValue(cell.getNumericCellValue());
							else if(thisCelltype == CellType.STRING)
								myCell.setCellValue(cell.getStringCellValue());
							else
								myCell.setCellFormula(cell.getCellFormula());
							
							//myCell.setCellStyle(cell.getCellStyle());
							
							CellStyle newStyle = myWorkBook.createCellStyle();
							newStyle.cloneStyleFrom(cell.getCellStyle());
							myCell.setCellStyle(newStyle);
							mySheet.autoSizeColumn(iCell);
						}
					}
				}
			}
		}
	}
}
 

다음은 결과 물입니다.

 

이상 입니다요~

 

728x90

'프로그래밍' 카테고리의 다른 글

OpenCV의 역사  (0) 2022.11.07
Nodejs 로 Http GET 메시지 테스트 하기  (1) 2022.09.10
아파치 POI로 하는 Excel 프로그래밍  (0) 2022.09.03
Guice - Google  (2) 2022.08.30
DisconnectedContext 오류  (0) 2022.04.28