본문으로 바로가기

[apache POI] 간단 사용법

category Java/Java 낙서장 2021. 1. 26. 22:15

apache POI는 언제 사용하는가?

아파치 POI(Apache POI)는 아파치 소프트웨어 재단에서 만든 라이브러리로서 마이크로소프트 오피스 파일 포맷을 순수 자바 언어로서 읽고 쓰는 기능을 제공한다.

 

JDK 호환성

POI 4.0 and later require JDK version 1.8 or later.

POI 3.11 and later 3.x versions require JDK version 1.6 or later.

POI 3.5 to 3.10 required the JDK version 1.5 or later. Versions prior to 3.5 required JDK 1.4+.

참고: poi.apache.org/devel/

 

Maven POM

참고로 Maven 프로젝트 생성시 사용한 archetype는 maven-archetype-quickstart (v.1.4) 이다.

 

그렇게 생성된 maven 프로젝트의 pom에 poi-ooxml dependency 하나만 추가한다.

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- jdk는 11버전 사용중입니다. -->
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- poi-ooxml 을 추가합니다 -->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>4.1.2</version>
    </dependency>
  </dependencies>

 

하나만 추가해도 Maven의 전이성 의존 덕분에 poi를 사용하기 위한 필요한 모든 jar 가 추가된다.

 

 

핵심 용어

1. apache POI 의 주요 클래스들은 주로 HSSF, XSSF 로 시작한다. 차이점은 아래와 같다.

   - HSSF - Excel 97(-2007) 파일 포맷을 사용할 때 사용 , ex) HSSFWorkbook, HSSFSheet
   - XSSF - Excel 2007 OOXML (.xlsx) 파일 포맷을 사용할 때 사용 , ex) XSSFWorkbook, XSSFSheet

 

2. Workbook, Sheet, Row, Cell

    - Workbook은 하나의 엑셀 파일을 의미
    - Sheet는 엑셀파일(= Workbook)의 시트를 의미
    - Row, Cell 은 Sheet 안에 있는 행과 열을 의미

 

 

엑셀 파일 생성하기

apache poi를 사용한 엑셀 파일을 생성은 아래와 같은 순서로 코드가 진행한다.

    1. workbook 을 생성한다.

    2. workbook 내에 sheet를 생성한다.

    3. sheet 내에 row를 생성한다.

    4. 하나의 row에 여러개의 cell을 생성한다. (= 하나의 행에 여러 열을 생성한다)

    5. 3과 4의 과정을 계속해서 반복한다.

 

코드:

package practice.test;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class PoiMakeExcel {

    public static String filePath = "C:\\poi_temp";
    public static String fileNm = "poi_making_file_test.xlsx";

    public static void main(String[] args) {

        // 빈 Workbook 생성
        XSSFWorkbook workbook = new XSSFWorkbook();

        // 빈 Sheet를 생성
        XSSFSheet sheet = workbook.createSheet("employee data");

        // Sheet를 채우기 위한 데이터들을 Map에 저장
        Map<String, Object[]> data = new TreeMap<>();
        data.put("1", new Object[]{"ID", "NAME", "PHONE_NUMBER"});
        data.put("2", new Object[]{"1", "cookie", "010-1111-1111"});
        data.put("3", new Object[]{"2", "sickBBang", "010-2222-2222"});
        data.put("4", new Object[]{"3", "workingAnt", "010-3333-3333"});
        data.put("5", new Object[]{"4", "wow", "010-4444-4444"});

        // data에서 keySet를 가져온다. 이 Set 값들을 조회하면서 데이터들을 sheet에 입력한다.
        Set<String> keyset = data.keySet();
        int rownum = 0;

        // 알아야할 점, TreeMap을 통해 생성된 keySet는 for를 조회시, 키값이 오름차순으로 조회된다.
        for (String key : keyset) {
            Row row = sheet.createRow(rownum++);
            Object[] objArr = data.get(key);
            int cellnum = 0;
            for (Object obj : objArr) {
                Cell cell = row.createCell(cellnum++);
                if (obj instanceof String) {
                    cell.setCellValue((String)obj);
                } else if (obj instanceof Integer) {
                    cell.setCellValue((Integer)obj);
                }
            }
        }

        try {
            FileOutputStream out = new FileOutputStream(new File(filePath, fileNm));
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

 

결과:

 

 

엑셀 파일 읽기

apache poi를 사용한 엑셀 파일을 읽는 것은 아래와 같은 순서로 코드가 진행한다.

    1. 이미 있는 엑셀 파일을 사용해서 workbook 인스턴스를 생성한다.

    2. 해당 workbook에서 원하는 sheet를 가져온다.

    3. sheet내에서 읽고자 하는 행 번호를 지정한다.

    4. row에 있는 모든 cell을 순회하면서 읽는다.

    5. 3과 4의 과정을 sheet 내의 모든 행을 읽을 때까지 반복한다.

 

package practice.test;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;

public class PoiReadExcel {

    public static String filePath = "C:\\poi_temp";
    public static String fileNm = "poi_reading_test.xlsx";

    public static void main(String[] args) {

        try {
            FileInputStream file = new FileInputStream(new File(filePath, fileNm));

            // 엑셀 파일로 Workbook instance를 생성한다.
            XSSFWorkbook workbook = new XSSFWorkbook(file);

            // workbook의 첫번째 sheet를 가저온다.
            XSSFSheet sheet = workbook.getSheetAt(0);

            // 만약 특정 이름의 시트를 찾는다면 workbook.getSheet("찾는 시트의 이름");
            // 만약 모든 시트를 순회하고 싶으면
            // for(Integer sheetNum : workbook.getNumberOfSheets()) {
            //      XSSFSheet sheet = workbook.getSheetAt(i);
            // }
            // 아니면 Iterator<Sheet> s = workbook.iterator() 를 사용해서 조회해도 좋다.

            // 모든 행(row)들을 조회한다.
            Iterator<Row> rowIterator = sheet.iterator();
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();

                // 각각의 행에 존재하는 모든 열(cell)을 순회한다.
                Iterator<Cell> cellIterator = row.cellIterator();

                while (cellIterator.hasNext()) {
                    Cell cell = cellIterator.next();

                    // cell의 타입을 하고, 값을 가져온다.
                    switch (cell.getCellType()) {
                        case NUMERIC:
                            System.out.print((int) cell.getNumericCellValue() + "\t"); //getNumericCellValue 메서드는 기본으로 double형 반환
                            break;
                        case STRING:
                            System.out.print(cell.getStringCellValue() + "\t");
                            break;
                    }
                }
                System.out.println();
            }
            file.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

예제 파일:

 

실행 결과:

 

 

엑셀 수식 넣기

엑셀이 복잡해질 수록 수식을 자주 사용하게 된다.

수식을 읽거나, 쓰는 법을 익혀본다.

package practice.test;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class PoiFormulaCell {

    public static String filePath = "C:\\poi_temp";
    public static String fileNm = "poi_formula_test.xlsx";

    public static void main(String[] args) {
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet("example");

        Row header = sheet.createRow(0);
        header.createCell(0).setCellValue("width");
        header.createCell(1).setCellValue("height");
        header.createCell(2).setCellValue("area");


        Row dataRow = sheet.createRow(1);
        dataRow.createCell(0).setCellValue(100);
        dataRow.createCell(1).setCellValue(100);
        dataRow.createCell(2).setCellFormula("A2*B2");

        try {
            FileOutputStream out = new FileOutputStream(new File(filePath, fileNm));
            workbook.write(out);
            out.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

 

결과:

 

 

엑셀 수식값 읽기

앞서 만든 파일(poi_formula_test.xlsx)의 수식이 적용된 area 값을 읽어 본다.

package practice.test;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;

public class PoiReadFormula {


    public static String filePath = "C:\\poi_temp";
    public static String fileNm = "poi_formula_test.xlsx";

    public static void main(String[] args) {

        try (FileInputStream file = new FileInputStream(new File(filePath, fileNm))) {

            XSSFWorkbook workbook = new XSSFWorkbook(file);

            FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();

            XSSFSheet sheet = workbook.getSheetAt(0);

            for (Row row : sheet) {
                if(row.getRowNum() == 0 ) { continue; }

                Iterator<Cell> cellIterator = row.cellIterator();

                while (cellIterator.hasNext()) {

                    Cell cell = cellIterator.next();
                    System.out.print("변환전: " + cell);
                    System.out.print(" / ");
                    System.out.println("변환후: " + evaluator.evaluateInCell(cell));

                    // 굳이 타입을 확인하고 싶다면 아래처럼 할 수도 있다.
                    /*
                    switch (evaluator.evaluateInCell(cell).getCellType()) {

                        case NUMERIC:
                            System.out.println(cell.getNumericCellValue() + "\t");
                            break;
                        case STRING:
                            System.out.println(cell.getStringCellValue() + "\t");
                            break;
                        case FORMULA:
                            //
                            break;
                    }
                    */
                }
                System.out.println("");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

 

evaluateInCell 메서드는 파라미터로 들어오는 cell이 수식이면 이를 평가하여(evaluate) 후 수정하여 다시 cell에 넣는다. 그래서 다음과 같은 결과가 나온다.

 

결과:

 

 

 

 

 

참고:

poi.apache.org/components/spreadsheet/quick-guide.html

https://poi.apache.org/components/spreadsheet/quick-guide.html

stackoverflow.com/questions/14315943/iteration-through-multiple-sheet-of-excel-in-java

'Java > Java 낙서장' 카테고리의 다른 글

파일들 zip으로 압축하기  (0) 2021.12.19
Java 로그(Log)를 위한 SLF4J & Logback - 기본  (0) 2021.02.09