1. Apache POI 라이브러리 추가
JAVA로 생성된 데이터를 Excel로 관리하려면 Apache POI 라이브러리가 필요하다.
gradle
dependencies {
implementation 'org.apache.poi:poi-ooxml:5.2.3'
}
2. Apache POI 주요 기능
- Workbook 생성 및 읽기
- new XSSFWorkbook(): .xlsx 형식의 Excel 파일을 생성합니다.
- WorkbookFactory.create(InputStream): .xls와 .xlsx 파일을 자동으로 열어 Workbook 객체를 생성합니다.
- Sheet 생성 및 가져오기
- workbook.createSheet(String name): 새로운 시트를 생성합니다.
- workbook.getSheetAt(int index): 인덱스를 통해 시트를 가져옵니다.
- workbook.getSheet(String name): 이름을 통해 시트를 가져옵니다.
- Row 생성 및 가져오기
- sheet.createRow(int rowNum): 새로운 행을 생성합니다.
- sheet.getRow(int rowNum): 기존 행을 가져옵니다.
- Cell 생성 및 값 설정
- row.createCell(int column): 새로운 셀을 생성합니다.
- **cell.setCellValue(String value)****: 셀에 문자열 값을 설정합니다.
- cell.setCellValue(double value): 셀에 숫자 값을 설정합니다.
- cell.setCellValue(Date value): 셀에 날짜 값을 설정합니다.
- Cell 값 읽기
- cell.getStringCellValue(): 셀의 문자열 데이터를 읽습니다.
- cell.getNumericCellValue(): 셀의 숫자 데이터를 읽습니다.
- 파일 저장 및 닫기
- workbook.write(OutputStream): 데이터를 Excel 파일로 저장합니다.
- workbook.close(): Workbook을 닫아 자원을 해제합니다.
- 셀 너비 자동 조정
- sheet.autoSizeColumn(int column): 셀 너비를 자동으로 데이터에 맞게 조정합니다.
- 셀 병합
- sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol)): 셀을 병합합니다.
3. 적용
코드
package com.coffebara.summaryBot.manager;
import com.coffebara.summaryBot.config.ExcelConfig;
import com.coffebara.summaryBot.entity.Member;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
@Slf4j
@Component
@RequiredArgsConstructor
public class ExcelManager {
private final ExcelConfig excelConfig;
// 엑셀 파일에서 name과 idCode를 찾아 데이터를 업데이트하거나 새로 추가
public void handleMemberData(List<Member> memberList) {
File excelFile = new File(excelConfig.getExcelPath());
try {
// 파일 잠금 여부를 확인하고, 파일이 잠겨 있다면 대기 후 재시도
if (!waitForFileToBeAvailable(excelFile, 5, 2000)) {
log.error("파일이 계속 사용 중이므로 엑셀 데이터 업데이트를 수행할 수 없습니다.");
return; // 파일 잠금 해제가 실패했으므로 메서드 종료
}
try (FileInputStream fis = new FileInputStream(excelFile); Workbook workbook = new XSSFWorkbook(fis)) {
Sheet targetSheet;
// 1. 시트가 하나인 경우
if (workbook.getNumberOfSheets() == 1) {
// 첫 번째 시트에 name과 idCode를 입력
Sheet firstSheet = workbook.getSheetAt(0);
log.info("첫 번째 시트에 name과 idCode 입력.");
insertFirstMemberDataToSheet(firstSheet, memberList); // 첫 번째 시트에 name과 idCode 입력
// 'before'라는 이름의 시트를 생성
targetSheet = workbook.createSheet("before");
log.info("'before' 시트를 생성했습니다.");
createHeaderRow(targetSheet); // 헤더 생성
insertMemberDataToSheet(targetSheet, memberList); // 데이터 입력
// 2. 시트가 2개 이상인 경우
} else if (workbook.getNumberOfSheets() >= 2) {
// 시트 개수 - 2를 사용하여 'after'라는 이름의 시트 생성
String sheetName = "after" + (workbook.getNumberOfSheets() - 1);
targetSheet = workbook.createSheet(sheetName);
log.info("'{}' 시트를 생성했습니다.", sheetName);
createHeaderRow(targetSheet); // 헤더 생성
insertMemberDataToSheet(targetSheet, memberList); // 데이터 입력
}
// 변경된 데이터를 엑셀 파일에 저장
try (FileOutputStream fos = new FileOutputStream(excelFile)) {
workbook.write(fos);
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
log.error("파일 잠금 해제를 기다리는 동안 인터럽트가 발생했습니다.", e);
}
}
// 첫 번째 시트에 name과 idCode를 입력하는 메서드
private void insertFirstMemberDataToSheet(Sheet sheet, List<Member> memberList) {
int rowNum = 3; // 데이터는 두 번째 행부터 시작
for (Member member : memberList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(member.getName()); // Name 입력
row.createCell(1).setCellValue(member.getIdCode()); // ID Code 입력
row.createCell(2).setCellValue(member.getAlly()); // Ally 입력
}
log.info("첫 번째 시트에 name과 idCode를 입력했습니다.");
}
// 헤더 행을 만드는 메서드
private void createHeaderRow(Sheet sheet) {
Row headerRow = sheet.createRow(0); // 첫 번째 행 생성
String[] headers = {"Name", "IdCode", "Ally", "Power", "Kill Points 4T", "Kill Points 5T", "Death"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]); // 헤더를 입력
}
}
// 새 행에 Member 데이터를 입력하는 메서드
private void insertMemberDataToSheet(Sheet sheet, List<Member> memberList) {
int rowNum = 1; // 데이터는 두 번째 행부터 시작
for (Member member : memberList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(member.getName());
row.createCell(1).setCellValue(member.getIdCode());
row.createCell(2).setCellValue(member.getAlly());
row.createCell(3).setCellValue(member.getPower());
row.createCell(4).setCellValue(member.getKillPoint4T());
row.createCell(5).setCellValue(member.getKillPoint5T());
row.createCell(6).setCellValue(member.getDeath());
}
}
public boolean isFileLocked(File file) {
// 파일이 쓰기 가능한지 체크
try (FileOutputStream fos = new FileOutputStream(file, true)) {
// 파일이 정상적으로 열리고 쓰기 가능하면 잠겨있지 않음
return false;
} catch (IOException e) {
// IOException 발생 시 파일이 잠겨있음
System.out.println("파일이 사용 중입니다: " + e.getMessage());
return true;
}
}
public boolean waitForFileToBeAvailable(File file, int retryCount, int waitTimeMs) throws InterruptedException {
for (int i = 0; i < retryCount; i++) {
if (!isFileLocked(file)) {
return true;
}
System.out.println("파일이 사용 중입니다. " + waitTimeMs + "ms 후 다시 시도합니다.");
Thread.sleep(waitTimeMs); // 지정된 시간 동안 대기
}
return false; // 재시도 횟수를 초과하면 false 반환
}
}
- handleMemberData
- insertFirstMemberDataToSheet
- createHeaderRow
- insertMemberDataToSheet
- isFileLocked
- waitForFileToBeAvailable
'Backend > 프로젝트' 카테고리의 다른 글
웹 서버 자동 점검 (2) | 2024.11.04 |
---|---|
갈아만든 임원 - 7 데이터 추출하기3 클립보드 접근 (1) | 2024.10.05 |
갈아만든 임원 - 6 데이터 추출하기2 OpenCV 전처리 작업 (2) | 2024.09.25 |
갈아만든 임원 - 5 데이터 추출하기2 OpenCV 설치 (1) | 2024.09.25 |
갈아만든 임원 - 4 데이터 추출하기 (Tesseract OCR) (0) | 2024.09.23 |