그럴때 사용하기 좋은 open source가 apache POI 입니다.
apache POI는 MS Office 전체를 대상으로 하고 있는 만큼, excel 구성만을 위해 사용한다면 다소 무거운 측면도 있지만, 그 만큼 강력하고 다양한 기능을 제공 하고 있습니다.
프로그램은 다음의 사이트에서 http://poi.apache.org/ 내용을 확인하신 후 download 항목에서 필요한 내용을 받으시면 됩니다. ( 글쓰는 시점의 안정버전은 POI 3.9 입니다. )
POI를 활용하여 excel 파일을 생성할 때 2003 version 의 *.xls, 2007 이상의 *.xlsx 파일 생성이 가능합니다. 2003 version 은 column 갯수의 제한(256) 이 있기 때문에 반듯이 구현해야 할 경우가 아니라면 xlsx 파일 형식으로 구성하는 것이 좋을 것 같습니다.
xls 파일은 HSSFWorkbook 클래스를 이용하고, xlsx 파일은 XSSFWorkbook class 를 생성하여 구성합니다.
poi 는 excel 기능의 sheet 생성, cell 의 data type, style 에 의한 꾸미기 기능 및
shape 을 구성하여 그리기 기능을 제공하거나, image ( jpeg,png,dib )를 sheet 내에 구성할 수도 있습니다.
api와 설명을 보면 cell merge나 수식, 합계 등의 기능등 excel 에서 제공하는 많은 기능들이 거의다 구현 가능한 것으로 보입니다.
이러한 기능중 excel 문서 생성기능을 중심으로 간략하게 예제를 구성해 보았습니다.
테스트용도의 코드 입니다.
public static void makeExcelWorkbookFileTest(String fileName) {
Map<String,Map<String,List<String>>> excelDataMap = new LinkedHashMap<String,Map<String,List<String>>>();
Random rd = new Random();
Map<String,List<String>> dataListMap = new LinkedHashMap<String,List<String>>();
for ( int y = 0; y < 20; y++ ) {
for ( int x = 0; x < 18; x++ ) {
String titleStr = "Title_FIRST_" + x ;
List<String> dataList = dataListMap.get(titleStr);
if ( dataList == null ) {
dataList = new ArrayList<String>();
dataListMap.put(titleStr,dataList);
}
if ( x % 2 == 0 ) {
dataList.add((Math.round(x*100000*rd.nextDouble())/100.0)+"");
} else {
dataList.add("X_" + x);
}
}
}
excelDataMap.put("TEST_FIRST_SHEET",dataListMap);
dataListMap = new LinkedHashMap<String,List<String>>();
for ( int y = 0; y < 20; y++ ) {
for ( int x = 0; x < 18; x++ ) {
String titleStr = "Title_SECOND_" + x ;
List<String> dataList = dataListMap.get(titleStr);
if ( dataList == null ) {
dataList = new ArrayList<String>();
dataListMap.put(titleStr,dataList);
}
if ( x % 1 == 0 ) {
dataList.add((Math.round(x*100000*rd.nextDouble())/100.0)+"");
} else {
dataList.add("X_" + x);
}
}
}
excelDataMap.put("TEST_SECOND_SHEET",dataListMap);
Workbook wb = getExcelWorkbook(excelDataMap);
File f = new File(fileName);
if ( f.exists() ) {
f.delete();
}
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream(f);
wb.write(fOut);
fOut.close();
fOut = null;
} catch (FileNotFoundException fne) {
fne.printStackTrace();
} catch ( IOException ioe ) {
ioe.printStackTrace();
} catch ( Exception ee ) {
ee.printStackTrace();
} finally {
if ( fOut != null ) {
try {
fOut.close();
} catch ( Exception eee ) {}
fOut = null;
}
}
}
// excel work bool을 생성하여 반환하기 위한 메소드 입니다.
// 데이터를 담고 있고 간단한 style적용을 위한 부분을 추가해 보았습니다.
public static Workbook getExcelWorkbook(Map<String,Map<String,List<String>>> excelDataMap) {
if ( excelDataMap == null || excelDataMap.size() == 0 ){
return null;
}
Workbook wb = new XSSFWorkbook();
boolean flag = false;
CellStyle titleStyle = wb.createCellStyle();
titleStyle.setBorderBottom(CellStyle.BORDER_THIN);
titleStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
titleStyle.setBorderLeft(CellStyle.BORDER_THIN);
titleStyle.setLeftBorderColor(IndexedColors.GREEN.getIndex());
titleStyle.setBorderRight(CellStyle.BORDER_THIN);
titleStyle.setRightBorderColor(IndexedColors.BLUE.getIndex());
titleStyle.setBorderTop(CellStyle.BORDER_MEDIUM_DASHED);
titleStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
titleStyle.setAlignment(CellStyle.ALIGN_CENTER);
titleStyle.setFillForegroundColor(IndexedColors.SEA_GREEN.getIndex());
titleStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
titleStyle.setWrapText(true);
CellStyle contentStyle = wb.createCellStyle();
contentStyle.setBorderBottom(CellStyle.BORDER_THIN);
contentStyle.setBottomBorderColor(IndexedColors.BLUE.getIndex());
contentStyle.setBorderLeft(CellStyle.BORDER_THIN);
contentStyle.setLeftBorderColor(IndexedColors.BLUE.getIndex());
contentStyle.setBorderRight(CellStyle.BORDER_THIN);
contentStyle.setRightBorderColor(IndexedColors.BLUE.getIndex());
contentStyle.setBorderTop(CellStyle.BORDER_THIN);
contentStyle.setTopBorderColor(IndexedColors.BLUE.getIndex());
contentStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
contentStyle.setFillPattern(CellStyle.SPARSE_DOTS);
for ( String sheetName : excelDataMap.keySet() ) {
if ( sheetName == null || sheetName.trim().length() == 0 ) {
continue;
} else {
flag = true;
}
Map<String,List<String>> dataListMap = excelDataMap.get(sheetName);
Sheet excelSheet = wb.createSheet(sheetName);
createExcelSheet(excelSheet,dataListMap,titleStyle,contentStyle);
}
if ( !flag ){
return null;
}
return wb;
}
// sheet 의 내용을 구성하기 위한 메소드 입니다.
private static boolean createExcelSheet(Sheet excelSheet
, Map<String,List<String>> dataListMap
,CellStyle titleStyle, CellStyle contentStyle ) {
int logCount = 0;
boolean result = false;
if ( excelSheet == null ){
return false;
}
if ( dataListMap == null || dataListMap.size() == 0 ) {
Row excelRow = excelSheet.createRow(0);
Cell excelCell = excelRow.createCell(0);
return true;
}
int columnIndex = 0;
int rowIndex = 0;
int rowSize = 0;
List<String> keyOrderList = new ArrayList<String>();
Row excelTitleRow = excelSheet.createRow(0);
for ( String keyStr : dataListMap.keySet() ) {
if ( keyStr == null || dataListMap.get(keyStr) == null || dataListMap.get(keyStr).size() == 0 ) {
continue;
}
keyOrderList.add(keyStr);
rowSize = (rowSize < dataListMap.get(keyStr).size() ? dataListMap.get(keyStr).size() : rowSize);
Cell excelCell = excelTitleRow.createCell(columnIndex++);
excelCell.setCellValue(keyStr);
if ( titleStyle != null ) {
excelCell.setCellStyle(titleStyle);
}
}
if ( columnIndex == 0 ) {
Cell excelCell = excelTitleRow.createCell(0);
return true;
}
rowIndex = 1;
for ( int y = 0; y < rowSize; y++ ) {
Row excelRow = excelSheet.createRow((rowIndex+y));
for ( int x = 0; x < columnIndex; x++ ) {
String valueStr = null;
if ( dataListMap.get(keyOrderList.get(x)).size() > y ) {
valueStr = dataListMap.get(keyOrderList.get(x)).get(y);
}
Cell excelCell = excelRow.createCell(x);
if ( valueStr != null ) {
if ( isNumberFormatStr(valueStr) ) {
excelCell.setCellValue(Double.valueOf(valueStr));
} else {
excelCell.setCellValue(valueStr);
}
}
if ( contentStyle != null ) {
excelCell.setCellStyle(contentStyle);
}
}
}
return result;
}
// 정규식 글에서 사용한 메소드 입니다.
private static boolean isNumberFormatStr(String str) {
if ( str == null ){
return false;
}
return str.matches("[+\\-]?([\\d]+([.][\\d]*)?|[.][\\d]+)([eE][+\\-]?[\\d]+)?");
}
앞서 소개한 사이트에는 다양한 예제가 있습니다.
이곳에서 간략하게 구현한 메소드는 excel download 기능을 구현할 때 사용할 수 있도록 데이터 구성과 sheet 구성에 초점을 맞추어 구성해 보았습니다.
Workbook 클래스의 write 메소드의 argument 는 OutputStream 입니다. Servlet 으로 구성할 때는 content type을 설정해 주시고, ServletOutputStream 객체를 넘겨 주시면 됩니다. 다만 위 소스에서는 stream 의 close를 명시적으로 해 주었지만, container 에서 close 를 자동관리하는 경우 close 메소드를 명시적으로 선언하지 않는 것이 좋습니다. 사용하고 있는 framework 혹은 container 에서 후처리 과정에서 stream 객체를 이용하여 무엇인가를 하고 있다면, close 메소드나, null 처리는 문제를 일으킬 수 있기 때문입니다.
댓글 없음:
댓글 쓰기