-
[Java] CSV 를 Excel 로 변환하는 Java 코드 예제Programming Language/Java 2023. 2. 8. 16:52반응형
최근 java 를 이용해서 .csv 파일을 Excel 로 변환 해야하는 일이 있었다.
그런데 단순히 csv 파일을 excel 의 하나의 sheet 로 변환하는 예제나 라이브러리는 몇 개 있었는데,
하나의 엑셀에 특정 컬럼들을 특정 시트에 작성하는 형태의 라이브러리 는 잘 나오지 않아서 개발 한 것을 적어둔다.1. 라이브러리 사용법
1.1. settings.xml 설정
<server> <id>github</id> <username>${github 유저명}</username> <password>${github 토큰}</password> </server>
1.1. 디펜던시 설정
<project> <dependencies> <dependency> <groupId>pe.fwani.convert</groupId> <artifactId>csv-to-excel</artifactId> <version>0.0.1-java11-SNAPSHOT</version> </dependency> </dependencies> <repositories> <repository> <id>github-fwani-releases</id> <url>https://github.com/fwani/fwani-maven-repo/raw/main/releases</url> <releases> <enabled>true</enabled> <updatePolicy>always</updatePolicy> </releases> </repository> <repository> <id>github-fwani-snapshots</id> <url>https://github.com/fwani/fwani-maven-repo/raw/main/snapshots</url> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> </snapshots> </repository> </repositories> </project>
1.2. 코드에서 사용 예제
import pe.fwani.convert.CsvToExcel; class Example { public static void main(String[] args) { var convertor = new CsvToExcel("origin.csv", "output.xlsx"); convertor.setWorkbookType(WorkbookType.SXSSF); convertor.convert(List.of( new Pair<>("시트1", List.of("col1", "col2")), new Pair<>("시트2", List.of("col1", "col3", "col4")) )); } }
2. 코드 설명
2.1. 디펜던시
먼저 csv 와 excel 을 다루기 위해 commons-csv 와 apache-poi 라이브러리를 설정 해준다.
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.9.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${poi.version}</version> </dependency>
2.2. 1개의 row 를 시트에 추가하는 코드
- csv 를 파싱한 List<String> 형태의 데이터를 sheet 에 추가하는 코드 이다.
- sheet 에 index 를 이용해 row 를 생성하고, 각 데이터를 row 에 생성한 cell 에 넣는다.
- 빈 데이터 ("") 인 경우 cell 을 생성하지 않도록 해서, 메모리 사용량을 줄이려고 했다. (실제로 체크는 못했다ㅜㅜ)
- dropAllNoneRow: true 일 경우 모든 데이터가 빈값("") 이면 엑셀의 row 를 생성 하지 않는다.private boolean addRow(Sheet sheet, int rowIndex, List<String> data, boolean dropAllNoneRow) { if (dropAllNoneRow && data.stream().allMatch(x -> x.equals(""))) { return false; } if (data.stream().allMatch(x -> x.equals(""))) { return true; } var row = sheet.createRow(rowIndex); var idx = new AtomicInteger(); data.forEach(x -> { var cellId = idx.getAndIncrement(); if (x.equals("")) return; row.createCell(cellId).setCellValue(x); }); return true; }
2.3. 모든 데이터를 엑셀 시트로 변환 코드
- commons-csv 의 CSVParser 를 이용하여 InputStream 을 분석한다.
- Workbook 을 생성하고, sheet 하나를 생성한다.
- Microsoft Excel 의 최대 row 개수인 1,048,576 개를 넘어가는 데이터는 버리고, Stream 으로 읽은 row 를 excel 에 넣는다.private void convertAll(String sheetName, boolean dropAllRow) throws IOException { try (var wb = getWorkbook(workbookType); var fileInputStream = new FileInputStream(source); var reader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); var writer = new FileOutputStream(destination) ) { var parser = csvFormat.parse(reader); var sheet = wb.createSheet(sheetName); int nowRowIdx = 0; for (var record : parser) { if (nowRowIdx == MAX_ROWS_OF_SHEET) { break; } addRow(sheet, nowRowIdx++, record.stream().toList(), dropAllRow); } wb.write(writer); } }
2.4. 각 시트별로 선택한 컬럼만 변환 하는 코드
- header 부분
- csv 첫 번째 라인을 header 로 처리한다.
- sheetNameAndColumnNamesPairs 를 순환하며, 각 sheet 를 생성하고, 시트별 선택된 컬럼의 인덱스를 찾아낸다.
- 헤더 라인을 sheet 0번 row 로 추가한다.
- row 부분
- 각 시트에 해당하는 index 를 이용하여 row 를 추가한다.
- 각 시트별로 Microsoft Excel 의 최대 row 개수인 1,048,576 개를 넘어가는 데이터는 버린다.private void convertEachSheetWithColumns(List<Pair<String, List<String>>> sheetNameAndColumnNamesPairs, boolean dropAllRow ) throws IOException { try (var wb = getWorkbook(workbookType); var fileInputStream = new FileInputStream(source); var reader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); var writer = new FileOutputStream(destination) ) { List<Pair<Sheet, List<Integer>>> sheetAndIndexes = new ArrayList<>(); List<Integer> rowIdxList = new ArrayList<>(); var parser = csvFormat.parse(reader); boolean isHeader = true; for (var record : parser) { if (isHeader) { // header isHeader = false; var header = record.toList().stream() .map(this::getCaseSensitiveString) .toList(); for (var sheetNameAndColumnNames : sheetNameAndColumnNamesPairs) { var sheet = wb.createSheet(sheetNameAndColumnNames.getKey()); var selectedIndexes = sheetNameAndColumnNames.getValue() .stream().map(x -> header.indexOf(getCaseSensitiveString(x))) .filter(x -> x >= 0) .toList(); addRow(sheet, 0, selectedIndexes.stream().map(header::get).toList(), dropAllRow); sheetAndIndexes.add(new Pair<>(sheet, selectedIndexes)); rowIdxList.add(1); } } else { // row if (rowIdxList.stream().allMatch(x -> x == MAX_ROWS_OF_SHEET || x == -1)) { break; } for (var i = 0; i < sheetAndIndexes.size(); i++) { var rowIdx = rowIdxList.get(i); if (rowIdx == MAX_ROWS_OF_SHEET) { continue; } var pair = sheetAndIndexes.get(i); if (addRow(pair.getKey(), rowIdx, pair.getValue().stream().map(record::get).toList(), dropAllRow)) { rowIdxList.set(i, rowIdx + 1); } } } } wb.write(writer); } }
3. 결과 예제
- 아래 왼쪽의 csv 파일을 오른쪽 엑셀로 변환
- 조건 : sheet1 - 번호/이름 컬럼, sheet2 - 학년/이름 컬럼을 저장4. 전체 코드
- 전체 코드는 csv-to-exce-java 에서 확인 할 수 있다.
반응형'Programming Language > Java' 카테고리의 다른 글
[Spring Boot3 + Jaeger] 스프링부트3에 예거 설정 추가 하는 방법 (0) 2024.07.31