Apache POI - Excel-Datei einlesen

Aus Byte-Welt Wiki

Mit dem Java-Framework Apache POI kann man recht komfortabel Microsoft Excel-Dateien auslesen und erzeugen (schreiben).

Hier ein vollständiges Beispiel zum Auslesen des Tabelleninhaltes einer beliebigen Excel-Datei. Das heißt, wir mit diesem Code das binäre Format (.xls) sowie das auf XML basierende Format .xslx einlesen.

Diese Klasse wurde mit der Apache License 2.0 veröffentlicht:

Excel-Datei einlesen

/*
 * ExcelFileReader.java
 *
 * Copyright (c) 2019 Gernot Segieth - wiki.byte-welt.net
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
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.ss.util.WorkbookUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
 * ExcelFileProxy liest die Excel-Datei ein und erzeugt ein tabellarisches
 * Abbild der Arbeitsmappe in Form von verschachtelten Listen.
 *
 * @author Gernot Segieth
 */
public class ExcelFileProxy {

    private File excelFile;
    private Workbook workbook;
    private FormulaEvaluator evaluator;
    private boolean isEmptyRow;

    /**
     * Erzeugt einen neuen Excel-Datei-Stellvertreter. ExcelFileProxy liest die
     * ihm übergebene Excel-Datei ein und erzeugt ein tabellarisches Abbild
     * davon.
     *
     * @param excelFile Die einzulesene Excel-Datei
     * @throws java.lang.ClassNotFoundException wenn eine Klasse nicht gefunden
     * wurde
     * @throws java.io.IOException wenn die Datei nicht eingelesen werde konnte
     * @throws org.apache.poi.openxml4j.exceptions.InvalidFormatException wenn
     * es Probleme mit dem Format der Datei oder der Verschlüsselung gibt
     */
    public ExcelFileProxy(File excelFile) throws ClassNotFoundException, IOException, InvalidFormatException {
        this.excelFile = excelFile;
        createWorkbook(excelFile);
    }

    /**
     * Gibt den Inhalt des übergebenen Tabellenblattes in einer geschachtelten
     * Liste zurück.
     *
     * @param sheetIndex der Index des Tabellenblattes
     * @return eine Liste aus Listen (Zeilen mit Spalten)
     */
    public List<List<?>> getSheetContent(int sheetIndex) {
        evaluator = workbook.getCreationHelper().createFormulaEvaluator();
        return getSheetContent(sheetIndex, 0, 0, 0, 0);
    }

    /**
     * Gibt den Inhalt des übergebenen Tabellenblattes in einer geschachtelten
     * Liste zurück.
     *
     * @param sheetIndex der Index des Tabellenblattes
     * @param firstRow in dieser Zeile mit dem Auslesen beginnen
     * @param firstCol in dieser Spalte mit dem Auslesen beginnen
     * @param lastRow letzte auszulesene Zeile
     * @param lastCol letzte auszulesene Spalte
     * @return eine Liste aus Listen (Zeilen mit Spalten)
     */
    public List<List<?>> getSheetContent(int sheetIndex, int firstRow, int firstCol, int lastRow, int lastCol) {
        evaluator = workbook.getCreationHelper().createFormulaEvaluator();
        Sheet sheet = workbook.getSheetAt(sheetIndex);
        List<List<?>> sheetData = new ArrayList<>();

        // Range festlegen (bei Übergabe 0 - ermitteln, sonst Range verarbeiten
        int lastRowNum;
        if (lastRow < 1) {
            lastRowNum = sheet.getLastRowNum();
        } else {
            lastRowNum = lastRow;
        }

        for (int actuallyRow = firstRow; actuallyRow <= lastRowNum; actuallyRow++) {

            Row row = sheet.getRow(actuallyRow); //leere Zeile kann NPE auslösen!

            if (row != null) {  //leere Zeile abfangen
                List<Object> rowData = new ArrayList<>();
                isEmptyRow = true; //Zeile erstmal als leer ansehen

                int lastColNum;
                if (lastCol < 1) {
                    lastColNum = row.getLastCellNum();
                } else {
                    lastColNum = lastCol;
                }

                for (int actuallyCol = firstCol; actuallyCol < lastColNum; actuallyCol++) {
                    Cell cell = row.getCell(actuallyCol);
                    rowData.add(getData(cell));
                }
                if (!isEmptyRow) { //leere Zeilen nicht aufnehmen
                    sheetData.add(rowData);
                }
            }
        }
        return sheetData;
    }

    /**
     * Schreibt Daten einer tabellenartig aufgebauten Liste in das Arbeitsblatt.
     *
     * @param sheetData die Daten aus der Liste, die in das Tabellenblatt
     * geschrieben werden sollen
     * @param sheetName Name des Tabellenblattes
     */
    public void setSheetContent(List<List<?>> sheetData, String sheetName) {
        String safeName = WorkbookUtil.createSafeSheetName(sheetName);
        CreationHelper createHelper = workbook.getCreationHelper();
        Sheet sheet = workbook.createSheet(safeName);

        for (int i = 0, j = sheetData.size(); i < j; i++) {
            Row row = sheet.createRow(i); //Zeile erzeugen
            List<?> rowList = sheetData.get(i); //Zeile holen
            for (int x = 0, y = rowList.size(); x < y; x++) {
                Cell cell = row.createCell(x); //Zelle erzeugen

                Object cellValue = rowList.get(x);
                if (cellValue instanceof Number) {
                    cell.setCellValue((Double) cellValue);
                } else if (cellValue instanceof Date) {
                    cell.setCellValue((Date) cellValue);
                } else if (cellValue instanceof Boolean) {
                    cell.setCellValue((Boolean) cellValue);
                } else if (cellValue instanceof String) {
                    cell.setCellValue(createHelper.createRichTextString(cellValue.toString()));
                }
            }
        }

    }

    /**
     * Gibt die Namen aller Tabellenblätter zurück.
     *
     * @return die Namen der Tabellenblätter
     */
    public String[] getSheetNames() {
        String[] sheetNames = new String[getNumberOfSheets()];
        for (int i = 0; i < sheetNames.length; i++) {
            sheetNames[i] = getSheetName(i);
        }
        return sheetNames;
    }

    /**
     * Gibt die Anzahl der Tabellenblätter in der Excel-Datei zurück.
     *
     * @return die Anzahl
     */
    public int getNumberOfSheets() {
        return workbook.getNumberOfSheets();
    }

    /**
     * Gibt den Namen des Tabellenblattes am übergebenen Index zurück.
     *
     * @param sheetIndex der Index
     * @return das Tabelleenblatt
     */
    public String getSheetName(int sheetIndex) {
        return workbook.getSheetName(sheetIndex);
    }

    /**
     * Setzt den Namen des Tabellenblattes am übergebenen Index.
     *
     * @param index der Index des Tabellenblattes in der Arbeitsmappe
     * @param sheetName der Name des Tabellenblattes
     */
    public void setSheetName(int index, String sheetName) {
        workbook.setSheetName(index, sheetName);
    }

    /**
     * Gibt den Index des aktiven Tabellenblattes zurück.
     *
     * @return der Index des aktiven Tabellenblattes
     */
    public int getActiveSheetIndex() {
        return workbook.getActiveSheetIndex();
    }

    /**
     * Setzt das aktive Tabellenblatt.
     *
     * @param index der Index des aktiven Tabellenblattes in der Arbeitsmappe
     */
    public void setActiveSheet(int index) {
        workbook.setActiveSheet(index);
    }

    /**
     * Gibt das Datum der letzten Änderung der Excel-Datei zurück.
     *
     * @return
     */
    public long lastModified() {
        return excelFile.lastModified();
    }

    /**
     * Gibt den genauen (einzigen) Pfad zur geladenen Excel-Datei zurück.
     *
     * @return Der Pfad zur geladenen Excel-Datei. //@throws java.io.IOException
     */
    public String getPath() /*throws IOException*/ {
        return excelFile.getAbsolutePath();
    }

    /**
     * Setzt den Pfad zur Excel-Datei und erzeugt ein Abbild des Workbooks (alle
     * Tabellenblätter).
     *
     * @param excelFile
     * @throws java.lang.ClassNotFoundException
     * @throws java.io.IOException
     * @throws org.apache.poi.openxml4j.exceptions.InvalidFormatException
     */
    public void setPath(File excelFile) throws ClassNotFoundException, IOException, InvalidFormatException {
        this.excelFile = excelFile;
        update();
    }

    /**
     * Liest das Workbook der Excel-Datei neu ein.
     *
     * @throws java.lang.ClassNotFoundException
     * @throws java.io.IOException
     * @throws org.apache.poi.openxml4j.exceptions.InvalidFormatException
     */
    public synchronized void update() throws ClassNotFoundException, IOException, InvalidFormatException {
        createWorkbook(excelFile);
    }

    public void createWorkbook(Map<String, List<List<?>>> sheetData) throws IOException {
        Set<String> keys = sheetData.keySet();
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String key = it.next();
            this.setSheetContent(sheetData.get(key), key);
        }
        createExcelFile(excelFile);
    }

    //erzeugt ein Workbook aus der übergebenen Excel-Datei
    private void createWorkbook(File file) throws ClassNotFoundException, IOException, InvalidFormatException {
        String filename = file.getName().toLowerCase();
        try {
            if (filename.endsWith(".xls")) {
                readHSSFWorkbook(file);
                //workbook = WorkbookFactory.create(file);
            } else if (filename.endsWith(".xlsx")) {
                readXSSFWorkbook(file);
            } else {
                throw new IllegalArgumentException("Ausnahmefehler: Die übergebene Datei " + file.getName() + " ist keine valide Microsoft Excel-Datei (*.xls bzw. *.xlsx)!");
            }
        } catch (IOException ioe) {
            Logger.getLogger(ExcelFileProxy.class.getName()).log(Level.SEVERE, null, ioe);
            throw new IOException("Ausnahmefehler: Die Datei " + file.getName() + " konnte nicht eingelesen werden!");
        } catch (EncryptedDocumentException ede) {
            Logger.getLogger(ExcelFileProxy.class.getName()).log(Level.SEVERE, null, ede);
            throw new EncryptedDocumentException("Ausnahmefehler: Die Datei " + file.getName() + " ist mit einem Passwort geschützt und kann daher nicht geöffnet werden!");
        } catch (NoClassDefFoundError err) {
            System.err.println(err.toString());
            throw new ClassNotFoundException("Schwerer Ausnahmefehler: Klassendefinition nicht gefunden!\nPrüfen Sie die Programmdateien und externen Bibliotheken auf Vollständigkeit!");
        }

    }

    private void readHSSFWorkbook(File file) throws IOException {
        POIFSFileSystem fs = new POIFSFileSystem(file);
        workbook = new HSSFWorkbook(fs.getRoot(), true);
        fs.close();
    }

    private void readXSSFWorkbook(File file) throws InvalidFormatException, IOException {
        OPCPackage pkg = OPCPackage.open(file);
        workbook = new XSSFWorkbook(pkg);
        pkg.close();
    }

    //Schreibt eine Arbeitsmappe in eine Exceldatei
    private void createExcelFile(File file) throws FileNotFoundException, IOException {
        try (FileOutputStream fileOut = new FileOutputStream(file)) {
            workbook.write(fileOut);
        } catch (FileNotFoundException fnfe) {
            throw new IOException("Ausnahmefehler: Die Datei " + file.getName() + " konnte nicht erzeugt werden!");
        } catch (IOException ioe) {
            throw new IOException("Ausnahmefehler: Die Datei " + file.getName() + " konnte nicht erzeugt werden!");
        }
    }

    //Liest die Daten aus einer Zelle und gibt sie zurück.
    private Object getData(Cell cell) {
        if (cell != null) {

            switch (cell.getCellType()) {
                case STRING:
                    String string = cell.getRichStringCellValue().getString();
                    if (string.isEmpty()) {
                        return null;
                    } else {
                        isEmptyRow = false;
                        return string.trim();
                    }
                case NUMERIC:
                    if (DateUtil.isCellDateFormatted(cell)) {
                        Date date = cell.getDateCellValue();
                        if (date.toString().isEmpty()) {
                            return null;
                        } else {
                            isEmptyRow = false;
                            return date;
                        }
                    } else {
                        Number number = cell.getNumericCellValue();
                        if (number.toString().isEmpty()) {
                            return null;
                        } else {
                            isEmptyRow = false;
                            return number;
                        }
                    }

                case BOOLEAN:
                    Boolean bool = (cell.getBooleanCellValue());
                    if (bool.toString().isEmpty()) {
                        return null;
                    } else {
                        isEmptyRow = false;
                        return bool;
                    }

                case _NONE:
                case FORMULA:
                case BLANK:
                case ERROR:
                default:
                    return null;
            }
        } else {
            return null;
        }
    }
}

Beispiel-Code für die Benutzung dieser Klasse

Erläuterungen zum Code

Dieser Beitrag wird derzeit noch bearbeitet. Der Text ist deshalb unvollständig und kann Fehler oder ungeprüfte Aussagen enthalten.