데이터를 엑셀파일에 저장하려면
cokr.xit.base.docs.xls.XLSWriter
를 생성- 워크시트를 생성
- 데이터를 설정할 셀(행과 열)의 선택
- 데이터를 설정
한다.
다음은 XLSWriter의 사용법을 XLSTest.java 참고하여 설명한다.
XLSWriter
XLSWriter는 다음과 같이 생성한다.
XLSWriter xlsx = new XLSWriter();
워크시트
엑셀파일에 데이터를 설정하려면 워크시트를 생성해야 한다.
워크시트는 인덱스(0부터 시작)로 생성하거나
xlsx.worksheet(0);
이름으로 만들 수 있다.
xlsx.worksheet("첫번째 워크시트");
이상은 다음처럼 간단히 할 수 있다.
XLSWriter xlsx = new XLSWriter().worksheet(0);
또는
XLSWriter xlsx = new XLSWriter().worksheet("워크시트 이름");
열 폭 자동설정
워크시트의 열들의 폭을 자동설정하려면 워크시트를 생성한 후
XLSWriter.trackWidth(int...)
를 호출한다.- 데이터 설정이 완료된 후
XLSWriter.autoWidth()
를 호출한다. 다음은 워크시트의 1, 2, 3, 4열의 데이터 길이를 추적하도록 설정하는 예다.
xlsx.trackWidth(0, 1, 2, 3);
또는
XLSWriter xlsx = new XLSWriter().worksheet(0).trackWidth(0, 1, 2, 3);
셀스타일
셀의 스타일 지정은 org.apache.poi.ss.usermodel.CellStyle
로 한다.
xit-docs는 CellStyle을 쉽게 쓸 수 있도록 Style
과 Format
을 제공한다.
다음은 Style과 Format을 초기화하는 예다.
Format format = new Format(xlsx);
CellStyle
numeric = format.n_nn0(), // 숫자 천단위 구분, 우측 정렬
datetime = format.yyyy_mm_dd_hh_mm_ss(), // 날짜 시간(yyyy-MM-dd HH:mm:ss)
blue = format.cellStyle(new Style() // 배경색 Blue, 폰트색 White인 CellStyle
.foregroundColor(IndexedColors.LIGHT_BLUE.getIndex())
.configure(style -> {
Font font = format.font();
font.setColor(IndexedColors.WHITE.getIndex());
style.font(font);
}));
- Format은 자주 쓰이는 CellStyle을 반환하는 메소드를 정의하고 있다.
- 그외의 다른 셀스타일은 Style을 생성 후 설정하고 Format.cellStyle(Style) 메소드로 CellStyle을 만들어 사용한다.
파일 쓰기
파일 저장
엑셀파일을 저장하려면 XLSWriter.write(String)
을 사용한다.
xlsx.write("C:\\my-dir\\엑셀파일.xlsx");
다운로드
'downloadView'로 다운로드하려면 콘트롤러의 메소드에서 다음과 같이 한다.
public ModelAndView doSomething() {
xlsx = ...;
....
return new ModelAndView("downloadView")
.addObject("download", xlsx.getDownloadable().setFilename("엑셀파일.xlsx"));
}
AbstractController를 상속하는 콘트롤러에서 직접 다운로드하려면 다음과 같이 한다.
public void doSomething(..., HttpServletResponse hresp) {
xlsx = ...;
....
download(xlsx.getDownloadable().setFilename("엑셀파일.xlsx"), hresp);
}
데이터 설정
행, 열, 셀의 선택
데이터를 설정하려면 데이터가 위치할 행과 열을 0부터 시작하는 인덱스로 선택한다.
xlsx.row(1) //두번째 행 선택
.col(1); //두번째 열 선택
위 코드는 다음처럼 간단히 할 수 있다.
xlsx.cell(1, 1);
행의 선택은 인덱스가 증가하는 방향으로만 할 수 있다. 즉,
xlsx.cell(127, 1); // 128번째 행 선택
위와 같이 128번째 행을 선택한 후 다시 이전 행을 선택할 수 없다.
xlsx.cell(5, 1); // 행의 인덱스가 감소하는 방향으로는 선택할 수 없다.
이는 XLSWriter가 의존하는 SXSSFWorkbook이 성능 상의 이유로 허용하지 않기 때문이다.
데이터 개별 설정
데이터와 스타일을 하나씩 설정하려면 XLSWriter.value(Object)
를 사용한다.
다음은 첫번째 행의 1열부터 3열까지 '테스트 0', 1234567890, 현재 날짜를 설정하는 예다.
Date now = new Date();
xlsx.row(0)
.col(0).value("테스트 0-0")
.col(1).value("테스트 0-1")
.col(2).value(1234567890)
.col(3).value(now)
숫자, 날짜시간 스타일을 적용하려면 다음과 같이 한다.
xlsx.row(0)
.col(0).value("테스트 0-0")
.col(1).value("테스트 0-1")
.col(2).value(1234567890).value(numeric) // 숫자 스타일
.col(3).value(now).value(datetime); // 날짜시간 스타일
행(row) 데이터 설정
행 하나에 여러 데이터와 스타일을 설정하려면 XLSWriter.rowValues(Iterable<?>)
를 사용한다.
다음은 두번째 행의 1열부터 '테스트 1', 2345678901, 현재 날짜를 설정하는 예다.
xlsx.cell(1, 0)
.rowValues(List.of("테스트 1-0", "테스트 1-1", 234567890, now));
숫자, 날짜시간 스타일을 적용하려면 다음과 같이 한다.
xlsx.cell(1, 0)
.rowValues(List.of("테스트 1-0", "테스트 1-1", 234567890, numeric/* 숫자 스타일 */, now, datetime /* 날짜시간 스타일*/));
스타일은 적용하려는 값의 다음에 표시한다.
Map의 데이터를 행 하나에 설정하려면 다음과 같이 한다.
DataObject map = new DataObject()
.set("field1", "테스트 2-0")
.set("field2", "테스트 2-1")
.set("field3", 34567890)
.set("field4", now);
xlsx.cell(2, 0)
.rowValues(map);
Map의 데이터를 선택/가공하거나 스타일을 적용하려면 다음과 같이 한다.
UnaryOperator<Object> concatField1_2 = obj -> { // 데이터 가공: field1 + field2
DataObject rec = (DataObject)obj;
return rec.string("field1") + " " + rec.string("field2");
};
xlsx.cell(2, 0)
.rowValues(
map,
format
.value(concatField1_2)
.style(blue),
"", // 공백
"field3", // 키 field3의 값
numeric, // 숫자 스타일
"field4", // 키 field4의 값
datetime // 날짜시간 스타일
)
.merge(0, 1) // 현재 행의 1, 2열 병합;
- 맵 다음에 지정하는 객체들의 갯수만큼 데이터를 설정한다.
- 맵의 키(예: field3, field4)를 지정하면 해당 키의 데이터를 설정한다.
- ""을 지정하면 해당 위치는 공란으로 설정한다.
- 데이터를 가공하여 설정하려면
Format.value(UnaryOperator)
를 사용한다.
객체 데이터 목록 설정
객체목록의 데이터들을 설정하려면 XLSWriter.values(Iterable, Function)
을 사용한다.
다음은 사용예다.
static class TestObject {
private String
field1,
field2;
private int field3;
private Date date;
//Getters & Setters
...
}
...
List<TestObject> objs = IntStream.range(0, 10).boxed()
.map(i -> new TestObject()
.setField1("테스트 " + (6 + i) + "-0")
.setField2("테스트 " + (6 + i) + "-1")
.setField3(i * 1000000)
.setDate(now)
)
.toList();
xlsx.cell(5, 0) // 6번째 행, 1번째 열 선택
.values(
objs, // 객체목록
obj -> List.of( // 객체 하나의 필드값들과 스타일을 반환하는 함수
obj.getField1(), // 객체의 field1 값
obj.getField2(), // 객체의 field2 값
obj.getField3(), // 객체의 field3 값
numeric, // field3을 숫자 스타일로 설정
obj.getDate(), // 객체의 date값
datetime // date값을 날짜시간 스타일로 설정
)
);
xlsx.autoWidth(); // 추적한 열들의 폭 자동설정
Map 데이터 목록 설정
맵 목록의 데이터들을 설정하려면 XLSWriter.values(Iterable, Object...)
을 사용한다.
다음은 사용예로 '두번째 시트'에 데이터를 설정하기로 한다.
List<DataObject> dataset = IntStream.range(0, 10).boxed()
.map(i -> (DataObject)new DataObject()
.set("field1", "테스트 " + (17 + i) + "-0")
.set("field2", "테스트 " + (17 + i) + "-1")
.set("field3", i * 1000000)
.set("date", now)
)
.toList();
xlsx.worksheet("두번째 시트").trackWidth(0, 1, 2, 3, 4); //'두번째 시트' 생성, 1 ~ 5열의 데이터 길이를 추적하도록
xlsx.cell(0, 0)
.values(dataset); // 맵의 모든 키에 대한 데이터들을 설정한다.
xlsx.cell(11, 0)
.values(
dataset,
format // 첫번째 열의 값과 CellStyle 설정
.value(concatField1_2)
.style(blue) // 셀의 배경과 글자색
.onCell(obj -> xlsx.merge(0, 1)), // 현재 행의 1, 2열을 병합
"", // 공백
"field3", // 키 field3의 값
numeric, // field3의 값을 숫자 스타일로 설정
"date", // 키 date의 값
datetime // date값을 날짜시간 스타일로 설정
);
이미지 메모 설정
셀에 이미지 메모를 설정하려면 Comment.setImageComment(String)
를 Format.onCell(Consumer)
로 다음과 같이 설정한다.
아래 예의 이미지 파일은 테스트 리소스로 제공되는 것을 사용한다.
String dir = "...",
path1 = dir + "4148020220006850A.jpg",
path2 = dir + "4148020220006869A.jpg";
dataset = IntStream.range(0, 10).boxed()
.map(i -> (DataObject)new DataObject()
.set("field1", "테스트 " + (27 + i) + "-0")
.set("field2", "테스트 " + (27 + i) + "-1")
.set("field3", i * 1000000)
.set("pic", i % 2 == 0 ? path1 : path2)
.set("date",now)
)
.toList();
CellStyle currency = format.cellStyle(new Style().dataFormat("#,##0 원").merge(Style.RIGHT));
Comment comment = new Comment(xlsx);
Format picFormat = format.of("pic") // 'pic' 필드를 위한 Format 생성
.value(obj -> {
String path = (String)((Map)obj).get("pic");
return path.substring(path.lastIndexOf("\\") + 1);
})
.onCell(obj -> comment.setImageComment((String)((Map)obj).get("pic")));
xlsx.cell(22, 0)
.values(
dataset,
format
.value(concatField1_2)
.style(blue)
.onCell(row -> xlsx.merge(0, 1)),
"",
"field3",
currency,
picFormat,
"date",
datetime
);
한 행에 Format이 여러 개 필요할 경우 Format.of(Object)
메소드나 new Format(xlsx).key(Object)
로 생성하여 지정한다.
CellDef
데이터를 설정할 때 각 열의 이름과 필드이름, 데이터 값, 스타일 등을 한 번에 정의할 때 CellDef
를 사용한다.
제목행을 설정할 때는 CellDef.header(List)
메소드를 사용한다.
데이터를 설정할 때는 CellDef.values(List)
메소드로 데이터/스타일을 받아 XLSWriter.values(List, Object...)
를 호출한다.
List<CellDef> cellDefs = List.of(
new CellDef().setLabel("제목").setValue( // 첫번째 열의 제목, 데이터, 스타일
format
.value(concatField1_2)
.style(blue)
.onCell(row -> xlsx.merge(0, 1))
),
CellDef.EMPTY, // 두번째 열은 빈 열
new CellDef().setLabel("금액").setField("field3").setValue(numeric), // 세번째 열의 제목, 데이터 필드, 스타일
new CellDef().setLabel("사진").setValue(picFormat), // 네번째 열의 제목과 이미지 설정
new CellDef().setLabel("등록일시").setField("date").setValue(datetime) // 다섯번째 열의 제목, 데이터 필드, 스타일
);
xlsx.cell(33, 0)
.rowValues(CellDef.header(cellDefs, null)) // 각 열의 제목 설정
.merge(0, 1)
.cell(34, 0)
.values(dataset, CellDef.values(cellDefs)); // CellDef를 이용해 dataset의 데이터 설정
xlsx.autoWidth(); // 추적한 열들의 폭 자동설정