실무주의 자바 POI 라이브러리
안녕하세요. 신입 개발자로 다시 글을 쓰게 되었습니다. 회사 생활 이야기는 다음 편에 글을 써보도록 하며 오늘은 실무에서 자주? 사용되는 자바 POI라이브러리 사용해서 부장님한테 사랑받는 개발자가 되어봅시다.
잠깐 실무 이야기를 하자면 매일 아침 지급데이터가 나오는데 대량이체시 한 번에 100개의 계좌 밖에 못한다면 지급데이터를 100개씩 나눠 여러 개의 엑셀 파일로 만들어야 했습니다.
방법은 많습니다. 100개로 페이징처리된 목록을 다운로드하거나 100개 이상의 엑셀 파일을 직접 나눠도 됩니다.
저는 n개의 데이터를 pivot 만큼 나누고 여러 개의 엑셀파일을 압축하여 하나의 파일로 다운가능하는 것을 목표로 했습니다.
구현 방식은 입맛에 맞게 수정하시어 사용하시길 바랍니다.
dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation group: 'org.apache.poi', name: 'poi', version: '5.0.0'
implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.0.0'
}
우선 튜토리얼로 엑셀파일을 다운로드하는 코드를 실행시켜 보며 POI라이브러 동장 방식을 이해해 보세요.
@GetMapping("fileexceldown")
public void getExcel(HttpServletRequest request, HttpServletResponse response) throws Exception{
int rowCount = 340;
int pivot = 100;
Workbook wb =null;
Sheet sheet;
File currDir;
List<String> fileDir = new ArrayList<>();
for (int i = 0; i < (rowCount/pivot)+1; i++) {
List<Account> accounts = new ArrayList<>();
for (int j = 0; j < pivot; j++) {
if(i*pivot+j> rowCount)break;
accounts.add(Account.builder().acctno(String.valueOf(i*pivot+j)).name("페이"+i).acctholder("kain"+i).build());
}
wb = new XSSFWorkbook();
sheet = wb.createSheet("첫번째 시트");
Row row = null;
Cell cell = null;
int rowNum = 0;
// Header
row = sheet.createRow(rowNum++);
cell = row.createCell(0);
cell.setCellValue("계좌번호");
cell = row.createCell(1);
cell.setCellValue("예금주");
cell = row.createCell(2);
cell.setCellValue("이름");
// Body
for (int q=0; q<pivot; q++) {
row = sheet.createRow(rowNum++);
cell = row.createCell(0);
cell.setCellValue(accounts.get(q).getAcctno());
cell = row.createCell(1);
cell.setCellValue(accounts.get(q).getAcctholder());
cell = row.createCell(2);
cell.setCellValue(accounts.get(q).getName());
}
currDir = new File("."); // 현재 프로젝트 경로를 가져옴
String path = currDir.getAbsolutePath();
String fileLocation = path.substring(0, path.length() - 1) + "temp"+(i+1)+".xls"; // 파일명 설정
fileDir.add(fileLocation);
try (FileOutputStream fileout = new FileOutputStream(fileLocation)){// 파일 생성
wb.write(fileout);// 엑셀파일로 작성
}catch (Exception e){
}finally {
wb.close();
}
}
}
Workbook 구현부부터 확인하시면 XSSFWorkbook객체로 생성하는데 이 객체는 메모리를 한 번에 올리고 파일을 작성한다고 합니다. 대용량 데이터를 엑셀로 만들 때 SXSSFWorkbook을 검색해 보세요.
workbook을 기준으로 sheet, head row, body row, 원한다면 footer도 추가하시면 됩니다. 이때 rowCount를 pivot으로 나눠 여러 개의 엑셀파일을 생성합니다. 생성위치는 현재 프로젝트 아래이고 파일명도 설정가능합니다.
다음은 여러개의 엑셀파일을 압축하는 방법입니다.
여러개의 엑셀데이터를 생성하고 하나의 파일로 압축합니다. 그리고 엑셀 파일을 제거해 줍니다.
public void getExcel(HttpServletRequest request, HttpServletResponse response) throws Exception{
int rowCount = 340;
int pivot = 100;
Workbook wb =null;
Sheet sheet;
File currDir;
currDir = new File("."); // 현재 프로젝트 경로를 가져옴
String path = currDir.getAbsolutePath();
List<String > fileNames = new ArrayList<>();
for (int i = 0; i < (rowCount/pivot)+1; i++) {
List<Account> accounts = new ArrayList<>();
for (int j = 0; j < pivot; j++) {
if(i*pivot+j>rowCount)break;
accounts.add(Account.builder().acctno(String.valueOf(i*pivot+j)).name("페이"+i).acctholder("kain"+i).build());
}
wb = new XSSFWorkbook();
sheet = wb.createSheet("첫번째 시트");
Row row = null;
Cell cell = null;
int rowNum = 0;
// Header
row = sheet.createRow(rowNum++);
cell = row.createCell(0);
cell.setCellValue("계좌번호");
cell = row.createCell(1);
cell.setCellValue("예금주");
cell = row.createCell(2);
cell.setCellValue("이름");
// Body
for (int q=0; q<accounts.size(); q++) {
row = sheet.createRow(rowNum++);
cell = row.createCell(0);
cell.setCellValue(accounts.get(q).getAcctno());
cell = row.createCell(1);
cell.setCellValue(accounts.get(q).getAcctholder());
cell = row.createCell(2);
cell.setCellValue(accounts.get(q).getName());
}
String fileLocation = path.substring(0, path.length() - 1) + "temp"+(i+1)+".xlsx"; // 파일명 설정
fileNames.add("temp"+(i+1)+".xlsx");
try (FileOutputStream fileout = new FileOutputStream(fileLocation)){
wb.write(fileout);
}catch (Exception e){
}finally {
wb.close();
}
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"alphabet.zip\""); // 변경된 부분
File zipFile = new File(path,"temp.zip" );
byte[] buf = new byte[4096];
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile))) { // 집 파일 다운로드
for (String fileName : fileNames) {
File excelFile = new File(path, fileName);
try (FileInputStream in = new FileInputStream(excelFile)) {
ZipEntry ze = new ZipEntry(excelFile.getName());
zipOut.putNextEntry(ze);
int len;
while ((len = in.read(buf)) > 0) {
zipOut.write(buf, 0, len);
}
zipOut.closeEntry();
}
// 엑셀 파일 삭제하는 로직
if(excelFile.exists()){
if(excelFile.delete()){
System.out.println(excelFile.getName()+" 파일 삭제되었음");
}else{
System.out.println(excelFile.getName()+" 파일 삭제되지 않음");
}
}
}
}
}
만약 어마어마한 대용량 데이터를 다루신다면 위 방식은 시스템에 부하를 줄 수 있습니다.
아래는 스트리밍 방식으로 파일을 다운로드하는 코드입니다.
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { // 스트리밍 방식으로 ZIP 파일을 직접 반환
for (String fileName : fileNames) {
File excelFile = new File(path, fileName);
try (FileInputStream in = new FileInputStream(excelFile)) {
ZipEntry ze = new ZipEntry(excelFile.getName());
zipOut.putNextEntry(ze);
int len;
while ((len = in.read(buf)) > 0) {
zipOut.write(buf, 0, len);
}
zipOut.closeEntry();
}
}
}
이 글을 통해 POI의 동작과정을 이해하고 적절하게 활용하시면 됩니다.
감사합니다.