LINUX.ORG.RU

Java, чтение Excel-файлов - посоветуйте библу

 ,


0

2

Сейчас использую


        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.16</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.16</version>
        </dependency>

вот такой класс-читальщик, который нашел на просторах инета

import org.apache.poi.ss.usermodel.*;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by bvn13 on 01.07.2017.
 */
public class ExcelReader {

    public static List<List<String>> readSpreadSheet(InputStream inputStream, Integer sheetNum) {
        Workbook workBook = null;
        try {
            workBook = WorkbookFactory.create(inputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        Sheet sheet = workBook.getSheetAt(sheetNum);
        List<List<String>> rowHolder = new ArrayList<List<String>>();
        int cellNum = sheet.getRow(1).getLastCellNum();

        for (int i = 0; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            List<String> cellHolder = new ArrayList<String>();

            for (int j = 0; j < row.getLastCellNum(); j++) {
                Cell cell = row.getCell(j);
                String cellValue = parseCellValue(workBook, cell);
                cellHolder.add(cellValue);
            }

            //add empty cells to the end if required
            while (cellHolder.size() < cellNum) {
                cellHolder.add(null);
            }
            rowHolder.add(cellHolder);
        }
        return rowHolder;
    }

    private static String parseCellValue(Workbook workBook, Cell cell) {
        FormulaEvaluator evaluator = workBook.getCreationHelper().createFormulaEvaluator();
        String cellValue = null;
        if (cell != null) {
            switch (cell.getCellTypeEnum()) {
                case STRING:
                    cellValue = cell.getRichStringCellValue().getString();
                    break;
                case NUMERIC:
                    if (DateUtil.isCellDateFormatted(cell)) {
                        cellValue = cell.getDateCellValue().toString();
                    } else {
                        cellValue = new Double(cell.getNumericCellValue()).toString();
                    }
                    break;
                case BOOLEAN:
                    cellValue = new Boolean(cell.getBooleanCellValue()).toString();
                    break;
                case FORMULA:
                    cellValue = evaluator.evaluate(cell).formatAsString();
                    break;
            }
        }
        return cellValue;
    }

    public static List<List<String>> readSpreadSheetWOnull(InputStream inputStream, Integer sheetNumber) {
        Workbook workBook = null;
        try {
            workBook = WorkbookFactory.create(inputStream);
            Sheet sheet = workBook.getSheetAt(sheetNumber);

            Iterator<Row> rowIter = sheet.rowIterator();

            List<List<String>> rowHolder = new ArrayList<List<String>>();
            while (rowIter.hasNext()) {
                Row row = (Row) rowIter.next();
                Iterator<Cell> cellIter = row.cellIterator();

                List<String> cellHolder = new ArrayList<String>();
                while (cellIter.hasNext()) {
                    Cell cell = (Cell) cellIter.next();
                    String cellValue = parseCellValue(workBook, cell);
                    cellHolder.add(cellValue);
                }
                rowHolder.add(cellHolder);
            }
            return rowHolder;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static List<List<String>> readSpreadSheetWOnull(InputStream inputStream) {
        return readSpreadSheetWOnull(inputStream, 0);
    }
    public static List<List<String>> readSpreadSheet(InputStream inputStream) {
        return readSpreadSheet(inputStream, 0);
    }
}

Если подсунуть 10-меговый файл, то даже на моем i7 8Gb вылетает с ошибкой:

Exception in thread "ru.bvn13.priceprocessor.workers.PriceLoaderFromFileWorker" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl.getNodeObject(DeferredDocumentImpl.java:1017)
	at com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl.synchronizeChildren(DeferredDocumentImpl.java:1755)
	at com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl.synchronizeChildren(DeferredElementNSImpl.java:158)
	at com.sun.org.apache.xerces.internal.dom.ParentNode.getFirstChild(ParentNode.java:229)
	at org.apache.xmlbeans.impl.store.Locale.loadNodeChildren(Locale.java:1402)
	at org.apache.xmlbeans.impl.store.Locale.loadNode(Locale.java:1445)
	at org.apache.xmlbeans.impl.store.Locale.loadNodeChildren(Locale.java:1403)
	at org.apache.xmlbeans.impl.store.Locale.loadNode(Locale.java:1445)
	at org.apache.xmlbeans.impl.store.Locale.loadNodeChildren(Locale.java:1403)
	at org.apache.xmlbeans.impl.store.Locale.loadNode(Locale.java:1445)
	at org.apache.xmlbeans.impl.store.Locale.loadNodeChildren(Locale.java:1403)
	at org.apache.xmlbeans.impl.store.Locale.loadNode(Locale.java:1445)
	at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1385)
	at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1370)
	at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.parse(SchemaTypeLoaderBase.java:370)
	at org.apache.poi.POIXMLTypeLoader.parse(POIXMLTypeLoader.java:144)
	at org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument$Factory.parse(Unknown Source)
	at org.apache.poi.xssf.usermodel.XSSFSheet.read(XSSFSheet.java:183)
	at org.apache.poi.xssf.usermodel.XSSFSheet.onDocumentRead(XSSFSheet.java:175)
	at org.apache.poi.xssf.usermodel.XSSFWorkbook.parseSheet(XSSFWorkbook.java:438)
	at org.apache.poi.xssf.usermodel.XSSFWorkbook.onDocumentRead(XSSFWorkbook.java:403)
	at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:190)
	at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:266)
	at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:185)
	at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:144)
	at ru.bvn13.priceprocessor.utils.ExcelReader.readSpreadSheet(ExcelReader.java:18)
	at ru.bvn13.priceprocessor.utils.ExcelReader.readSpreadSheet(ExcelReader.java:102)
	at ru.bvn13.priceprocessor.workers.PriceLoaderFromFileWorker.readFileNewExcelFormat(PriceLoaderFromFileWorker.java:409)
	at ru.bvn13.priceprocessor.workers.PriceLoaderFromFileWorker.loadFile(PriceLoaderFromFileWorker.java:294)
	at ru.bvn13.priceprocessor.workers.PriceLoaderFromFileWorker.startLoadingFile(PriceLoaderFromFileWorker.java:206)
	at ru.bvn13.priceprocessor.workers.PriceLoaderFromFileWorker.job(PriceLoaderFromFileWorker.java:96)
	at ru.bvn13.priceprocessor.workers.AbstractWorker.run(AbstractWorker.java:52)

А есть что-то менее прожорливое, чтобы читать?

★★★★★

А ты жаве дал эту память?

Legioner ★★★★★ ()

Откуда запускаешь? Из IDE, из maven, упакованный jar командой java?

Легче всего быстро посмотреть, сколько памяти использует JVM, через jconsole.

BattleCoder ★★★★★ ()
Ответ на: комментарий от ya-betmen

А если заранее неизвестно, сколько памяти потребуется? Пользователь может захотеть открыть и 1 мегабайт и 15-ть мегабайт файлы. При том что оперативки допустим 5-кратный запас. Сейчас на кону остатки моего уважения к Java как к платформе.

I-Love-Microsoft ★★★★★ ()
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)
Ответ на: комментарий от I-Love-Microsoft

Вообще размер хипа ограничивается с помощью Xmx, если его не указывать то есть дефолтное поведение, которое зависит от server/client mode, от объема свободной памяти и от JVM из которой все это добро запущено.
Обычно всякие мавены и прочий сброд указывает этот флажок при запуске кода.

Deleted ()
Ответ на: комментарий от I-Love-Microsoft

Ты не понял, либо человек вписал в команду что-то совсем миллипизерное либо у него код кривой, прежде чем разбираться со вторым следует исключить первое. Судя по ошибке гц не хватает памяти чтобы мусор подбирать.

ya-betmen ★★★★★ ()
Ответ на: комментарий от ya-betmen

ГЦ всё хватает, просто делает это очень долго - дольше чем разрешено. Это не обязательно нехватка памяти, может быть другой тип ГЦ более эффективен при таком профиле нагрузки.

FeyFre ★★★★ ()
Ответ на: комментарий от bvn13

Почему 1024m, если у тебя 8 гигабайтов? Для начала попробуй 6144m, чтобы убедиться, что оно вообще заработает, потом уменьшай, параллельно мониторя потребление хипа (например через jconsole), пока не добьёшься желаемого баланса между потреблением памяти и нагрузкой на GC.

Legioner ★★★★★ ()
Ответ на: комментарий от FeyFre

начал грузиться с параметрами JVM: -Xmx4096m -XX:+UseG1GC. Но это я должен каждый раз угадывать? Нельзя как-то универсально чтоб?

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

Тут есть почитать: тыц
Ваш случай может быть тыц , маловероятно правда. Нужно смотреть логи ГЦ.

FeyFre ★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

А если заранее неизвестно, сколько памяти потребуется? Пользователь может захотеть открыть и 1 мегабайт и 15-ть мегабайт файлы.

Максимальное ограничение памяти это столько, больше чего жава не скушает. Это не значит, что если ты укажешь -Xmx=64g, что она автоматом захавает 64 гига. Она начнёт с 16 МБ по умолчанию, насколько я помню. Когда посчитает, что этого мало — попросит у ОС новой памяти и увеличит хип. Вот что жава не умеет делать, к сожалению, так это отдавать память назад, даже если пик потребления уже прошёл, это действительно минус жавы, хотя может уже научилась.

Legioner ★★★★★ ()
Ответ на: комментарий от Legioner

странно. это в момент ошибки ГЦ память отдалась. сейчас, без ошибки, - память остается у JVM

bvn13 ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

Сгенерь HTML-таблицу на 10 мегабайов зазипованного HTML и открой её в браузере, потом расскажи, сколько он сожрёт.

Legioner ★★★★★ ()

А есть что-то менее прожорливое, чтобы читать?

Ещё посмотри сюда, если хочешь с минимумом памяти обрабатывать.

Legioner ★★★★★ ()
Ответ на: комментарий от Legioner

там только для записи же? я ничего не записываю

bvn13 ★★★★★ ()

Для XLS/XLSX в POI есть поточный парсер, который не разбирает весь документ в память. Используй его, потому как xmlbeans очень неоптимален по памяти.

maxcom ★★★★★ ()
Последнее исправление: maxcom (всего исправлений: 1)
Ответ на: комментарий от Legioner

я сейчас смотрю https://github.com/monitorjbl/excel-streaming-reader

Вроде как обертка над apache poi. Но блин! Оно не может мой файл прочитать. Видит какую-то несуществующую колонку, читает из нее несуществующий текст, парсит его как Integer, и валится.

https://i.imgur.com/Ln8QAOE.png

bvn13 ★★★★★ ()
Ответ на: комментарий от Radjah

да вообще странная ситуация. если оставляю первые строк 100, то все норм грузится. Если весь файл - ошибка. Есть возможность, протестить?

bvn13 ★★★★★ ()

10-меговый файл в xlsx это вообще то дофига. это, наверно, где-то 50-70 метровый sheet1.xml файл с данными. Формат экселя не предназначен для потоковой работы - тоесть для работы с таким объёмом нужно много памяти. тем более из-под java.

vtVitus ★★★★★ ()
Ответ на: комментарий от bvn13

У тебя там случайно вложений типа картинок/ссылок/прочего не имеется? А то недавно пришлось разбирать сложный хлсх через пои, так там довольно нетривально сопоставлять картинки с ячейками в которых они лежат и появляются виртуальные колонки с ид этих картинок.

ya-betmen ★★★★★ ()
Ответ на: комментарий от bvn13

нет не вижу. валится на первом row, а ты смотришь 4. Ошибка кстати понятная - у тебя все данные текстовые, но poi считает что у тебя в 4 и 7 колонке числовые данные. Таких ошибок в poi было тонны когда формат 2007 вводили. В твоём случае можно тупо везде проставлять формат строки аля cell.setCellType(Cell.CELL_TYPE_STRING) перед считыванием.

Ну и багу можно запостить с примером xlsx.

vtVitus ★★★★★ ()
Последнее исправление: vtVitus (всего исправлений: 1)
Ответ на: комментарий от vtVitus

Если использовать потоковое чтение, то ошибка возникает в момент for (Row row : sheet) {. Как на это можно повлиять?

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

хз. потоковая работа с xlxs от лукавого. хочется потоков используй csv. мой совет остаётся прежним cell.setCellType(Cell.CELL_TYPE_STRING) :-)

vtVitus ★★★★★ ()

Жавы не знаю, но по коду вижу, что либа для чтения предоставляет возможность потокового чтения через итератор, а ты берешь, все это говнище читаешь и грузишь в память через лист. Сначала программировать научись.

nikolnik ★★★ ()
Ответ на: комментарий от nikolnik

чтоб давать советы необходимо знать subj - валится в этой строке WorkbookFactory.create(inputStream) .

vtVitus ★★★★★ ()
Ответ на: комментарий от bvn13

в какой момент, если он row из итератора не может получить?

при использовании этого ? https://github.com/monitorjbl/excel-streaming-reader

что-то сомнительно. ты бы уже 100 лет назад выложил бы свой эксельник и код, а то это всё походит на гадание.

vtVitus ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.