Java에서 엑셀(Excel) 파일(.xlsx) 제어하기, Apachi POI

계속 작성 중인 문서입니다. 지속적으로 내용을 보강하겠습니다.

Java에서 액셀 파일은 어떻게 제어하지?


Java의 강점은 오래된 역사만큼이나 다양한 라이브러리가 존재한다는 것인데 엑셀 파일에 대한 제어를 제공하는 라이브러리 또한 존재한다. Apache POI는 엑셀 뿐만 아니라 모든 MS 오피스 문서에 대한 파일 제어를 지원한다.

다운로드는?


Apache POI는 꽤나 덩치가 큰 라이브러리이다. 아래 순서대로 다운로드한다.

  • https://poi.apache.org에 방문하여 왼쪽 메뉴에서 Download 메뉴를 클릭한다.
  • 화면 중앙의 Binary Distribution 항목의 poi-bin-3.10-FINAL-20140208.tar.gz를 클릭하여 다운로드한다.
  • 압축 해제 후 모든 .jar 파일을 프로젝트에 임포트하면 라이브러리르 사용할 준비가 완료된다. 임포트할 파일은 아래와 같다.
    • poi-3.10-FINAL-20140208.jar
    • poi-excelant-3.10-FINAL-20140208.jar
    • poi-ooxml-3.10-FINAL-20140208.jar
    • poi-ooxml-schemas-3.10-FINAL-20140208.jar
    • poi-scratchpad-3.10-FINAL-20140208.jar
      • dom4j-1.6.1.jar
      • stax-api-1.0.1.jar
      • xmlbeans-2.3.0.jar

.xlsx 파일 불러오기


OPCPackage opcPackage = OPCPackage.open(new File("C:/SOMEFILE.xlsx"));
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
opcPackage.close();

XSSFSheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
  for (Cell cell : row) {
      // 처리 로직 작성
    }
  }
}
  • .xlsx 파일은 수용할 수 있는 최대 로우 수가 1,048,576개이다. 대용량 파일을 제어하려면 효율적인 메모리 관리가 중요하다. OPCPackage.open() 메써드로 파일 로드시 FileInputStream 클래스 대신 File 클래스를 사용하면 메모리 낭비를 줄일 수 있다.
  • .xlsx 파일 제어는 Apachi POI가 제공하는 XSSF 모드를 사용해서 접근해야 한다.(.xls 파일은 HSSF 모드로 접근한다.)
  • 파일 제어는 Excel 애플리케이션이 사용하는 용어와 동일한 Workbook, Sheet, Row, Cell 클래스로 접근할 수 있다.

.xlsx 파일 저장하기


Workbook workbook = new SXSSFWorkbook();
SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet("SOMESHEET");
// 처리 로직 작성
FileOutputStream fos = new FileOutputStream("C:/SOMEWORKBOOK.xlsx");
workbook.write(fos);
fos.close();
  • 앞서 파일을 불러오는 것은 XSSF 모드를 사용했다면 저장은 SXSSF 모드를 사용해야 한다.

List 오브젝트를 .xlsx 파일로 저장하기


단순히 특정 데이터베이스의 테이블 데이터 전체를 .xlsx 파일로 저장하려면? 기업 전산실의 경우 현업 담당자들이 수시로 요청하는 업무이기도 하다. Orange 또는 Toad같은 툴을 이용하여 저장할 수도 있지만 개발자에게는 이 또한 귀찮은 반복 업무일 뿐이다. Java로 작성해보자. 데이터베이스 테이블의 정보를 그대로 담을 POJO 클래스가 이미 설계되어 있다고 가정하면 DAO가 반환하는 데이터의 타입은 List<?>가 될 것이다. 이상의 가정으로 List 오브젝트를 .xlsx 파일로 변환하여 저장하는 유틸리티 클래스를 아래와 같이 만들어봤다.

package com.jsonobject.apachepoitest;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

public class ExcelUtil {
	public static <T> void createXlsx(List<T> pojoObjectList, String filePath) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException {
		Workbook workbook = new SXSSFWorkbook();
		SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet();
		for (int i = 0; i < pojoObjectList.size(); i++) {
			Row row = sheet.createRow(i);
			if (i == 0) {
				RowFilledWithPojoHeader(pojoObjectList.get(i), row);
			} else {
				RowFilledWithPojoData(pojoObjectList.get(i), row);
			}
		}
		FileOutputStream fos = new FileOutputStream(filePath + pojoObjectList.get(0).getClass().getSimpleName().toUpperCase() + "_" + System.currentTimeMillis() + ".xlsx");
		workbook.write(fos);
		fos.close();
	}

	public static <T> void generateXlsx(List<T> pojoObjectList, String templateFileName, String sheetName, String outputFilePath, String outputFileName) throws InvalidFormatException, IOException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		FileUtils.copyFile(new File(templateFileName), new File(outputFilePath + "TEMP_" + outputFileName));
		OPCPackage opcPackage = OPCPackage.open(new File(outputFilePath + "TEMP_" + outputFileName));
		XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
		XSSFSheet sheet = workbook.getSheet(sheetName);
		for (int i = 0; i < pojoObjectList.size(); i++) {
			Row row = sheet.createRow(i);
			if (i == 0) {
				RowFilledWithPojoHeader(pojoObjectList.get(i), row);
			} else {
				RowFilledWithPojoData(pojoObjectList.get(i), row);
			}
		}
		FileOutputStream fos = new FileOutputStream(outputFilePath + outputFileName);
		workbook.write(fos);
		fos.close();
		opcPackage.close();
		FileUtils.deleteQuietly(new File(outputFilePath + "TEMP_" + outputFileName));
		return;
	}

	private static Row RowFilledWithPojoHeader(Object pojoObject, Row row) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Field[] fields = pojoObject.getClass().getDeclaredFields();
		int fieldLength = fields.length;
		for (int i = 0; i < fieldLength; i++) {
			String cellValue = fields[i].getName().toUpperCase();
			row.createCell(i).setCellValue(cellValue);
		}
		return row;
	}

	private static Row RowFilledWithPojoData(Object pojoObject, Row row) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Field[] fields = pojoObject.getClass().getDeclaredFields();
		int fieldLength = fields.length;
		for (int i = 0; i < fieldLength; i++) {
			Method method = pojoObject.getClass().getMethod("get" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1));
			String cellValue;
			String returnType = method.getReturnType().getName();
			if (returnType.equals("org.joda.time.DateTime")) {
				Object dateTime = method.invoke(pojoObject);
				if (dateTime == null) {
					cellValue = "";
				} else {
					cellValue = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print((DateTime) dateTime);
				}
			} else {
				cellValue = (String) method.invoke(pojoObject);
			}
			row.createCell(i).setCellValue(cellValue);
		}
		return row;
	}

	public static String getCellValue(Row row, int cellIndex) {
		Cell cell = row.getCell(cellIndex);
		if (cell == null) {
			return null;
		}
		cell.setCellType(Cell.CELL_TYPE_STRING);
		return cell.getStringCellValue();
	}

	public static String getCellValue(Object object) {
		if (object == null) {
			return "";
		}
		return object.toString();
	}
}

사용 예는 아래와 같다.

ExcelUtil.createXlsx(someListObject, "/var/data/");

참고 글



저작자 표시 비영리 동일 조건 변경 허락
신고