package egovframework.util.excel; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @Slf4j public class ExcelHandler { public List handleExcelUpload(List mFiles, Class clazz) { List dataList = new ArrayList<>(); mFiles.forEach(file -> { try (InputStream inputStream = file.getInputStream()) { dataList.addAll(parseExcel(inputStream, clazz)); } catch (IOException e) { throw new RuntimeException(e); } }); return dataList; } private List parseExcel(InputStream inputStream, Class clazz) throws IOException { Workbook workbook = new XSSFWorkbook(inputStream); Sheet sheet = workbook.getSheetAt(0); // 첫 번째 시트를 가져옴 // 헤더 정보 추출 Row headerRow = sheet.getRow(0); List headers = StreamSupport.stream(headerRow.spliterator(), false) .map(Cell::getStringCellValue) .collect(Collectors.toList()); List dataList = StreamSupport.stream(sheet.spliterator(), false) .skip(1) // 첫 번째 행은 헤더이므로 건너뜁니다. .filter(this::isRowNotEmpty) // 빈 행이 아닌 경우에만 처리합니다. .map(row -> mapRowToDto(row, clazz, headers)) .collect(Collectors.toList()); workbook.close(); return dataList; } private boolean isRowNotEmpty(Row row) { Iterator cellIterator = row.cellIterator(); while (cellIterator.hasNext()) { Cell cell = cellIterator.next(); if (cell.getCellType() != CellType.BLANK) { return true; // 빈 셀이 아닌 경우에만 true를 반환합니다. } } return false; // 모든 셀이 비어 있으면 false를 반환합니다. } private T mapRowToDto(Row row, Class clazz, List excelHeaderList) { T dataDto; try { dataDto = clazz.getDeclaredConstructor().newInstance(); Iterator cellIterator = row.cellIterator(); while (cellIterator.hasNext()) { Cell cell = cellIterator.next(); String excelHeaderName = excelHeaderList.get(cell.getColumnIndex()); //각 필드를 순회하며 커스텀 어노테이션인 ExcelHeader값에 맞게 값을 넣어줌 Field[] dtoFields = clazz.getDeclaredFields(); for (Field field : dtoFields) { if (field.isAnnotationPresent(ExcelColumn.class)) { ExcelColumn annotation = field.getAnnotation(ExcelColumn.class); if (Objects.requireNonNull(annotation).headerName().equals(excelHeaderName)) { field.setAccessible(true); setFieldValue(field, dataDto, cell); break; } } } } } catch (Exception e) { throw new RuntimeException(e); } return dataDto; } private void setFieldValue(Field field, T dataDto, Cell cell) throws IllegalAccessException { Class fieldType = field.getType(); field.setAccessible(true); if (fieldType == int.class || fieldType == Integer.class) { field.set(dataDto, (int)cell.getNumericCellValue()); } else if (fieldType == long.class || fieldType == Long.class) { field.set(dataDto, (long)cell.getNumericCellValue()); } else if (fieldType == double.class || fieldType == Double.class) { field.set(dataDto, cell.getNumericCellValue()); } else if (fieldType == boolean.class || fieldType == Boolean.class) { field.set(dataDto, cell.getBooleanCellValue()); } else { // if (fieldType == String.class) { DataFormatter formatter = new DataFormatter(); field.set(dataDto, formatter.formatCellValue(cell)); } // 다른 타입에 따른 맵핑을 추가할 수 있습니다. } }