OutOfMemoryException при экспорте данных [дубликат]

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

4
задан Christian.K 21 September 2015 в 08:54
поделиться

3 ответа

SDK OpenXML является правильным инструментом для этого задания, но вам нужно быть осторожным, чтобы использовать подход SAX (простой API для XML), а не подход DOM . Из связанной статьи wikipedia для SAX:

Если DOM работает с документом в целом, SAX-парсеры последовательно работают с каждой частью XML-документа

значительно уменьшает объем памяти, потребляемой при обработке больших файлов Excel.

Здесь есть хорошая статья - http://polymathprogrammer.com/2012/ 08/06 / how-to-right-use-openxmlwriter-to-write-large-excel-files /

Адаптировано из этой статьи, вот пример, который выводит строки 115k с 30 столбцами :

public static void LargeExport(string filename)
{
    using (SpreadsheetDocument document = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
    {
        //this list of attributes will be used when writing a start element
        List<OpenXmlAttribute> attributes;
        OpenXmlWriter writer;

        document.AddWorkbookPart();
        WorksheetPart workSheetPart = document.WorkbookPart.AddNewPart<WorksheetPart>();

        writer = OpenXmlWriter.Create(workSheetPart);            
        writer.WriteStartElement(new Worksheet());
        writer.WriteStartElement(new SheetData());

        for (int rowNum = 1; rowNum <= 115000; ++rowNum)
        {
            //create a new list of attributes
            attributes = new List<OpenXmlAttribute>();
            // add the row index attribute to the list
            attributes.Add(new OpenXmlAttribute("r", null, rowNum.ToString()));

            //write the row start element with the row index attribute
            writer.WriteStartElement(new Row(), attributes);

            for (int columnNum = 1; columnNum <= 30; ++columnNum)
            {
                //reset the list of attributes
                attributes = new List<OpenXmlAttribute>();
                // add data type attribute - in this case inline string (you might want to look at the shared strings table)
                attributes.Add(new OpenXmlAttribute("t", null, "str"));
                //add the cell reference attribute
                attributes.Add(new OpenXmlAttribute("r", "", string.Format("{0}{1}", GetColumnName(columnNum), rowNum)));

                //write the cell start element with the type and reference attributes
                writer.WriteStartElement(new Cell(), attributes);
                //write the cell value
                writer.WriteElement(new CellValue(string.Format("This is Row {0}, Cell {1}", rowNum, columnNum)));

                // write the end cell element
                writer.WriteEndElement();
            }

            // write the end row element
            writer.WriteEndElement();
        }

        // write the end SheetData element
        writer.WriteEndElement();
        // write the end Worksheet element
        writer.WriteEndElement();
        writer.Close();

        writer = OpenXmlWriter.Create(document.WorkbookPart);
        writer.WriteStartElement(new Workbook());
        writer.WriteStartElement(new Sheets());

        writer.WriteElement(new Sheet()
        {
            Name = "Large Sheet",
            SheetId = 1,
            Id = document.WorkbookPart.GetIdOfPart(workSheetPart)
        });

        // End Sheets
        writer.WriteEndElement();
        // End Workbook
        writer.WriteEndElement();

        writer.Close();

        document.Close();
    }
}

//A simple helper to get the column name from the column index. This is not well tested!
private static string GetColumnName(int columnIndex)
{
    int dividend = columnIndex;
    string columnName = String.Empty;
    int modifier;

    while (dividend > 0)
    {
        modifier = (dividend - 1) % 26;
        columnName = Convert.ToChar(65 + modifier).ToString() + columnName;
        dividend = (int)((dividend - modifier) / 26);
    }

    return columnName;
}
15
ответ дан petelids 19 August 2018 в 11:28
поделиться
  • 1
    Это лучшее решение когда-либо !!!! Файл экспорта 500 000 x 400 столбцов занимает среднюю память 60 Мбайт – Gianluigi Liguori 13 October 2015 в 18:05
  • 2
    Я рад, что смог помочь @GianluigiLiguori – petelids 14 October 2015 в 12:52
  • 3
    @petelids пробовал ваш код и работает! Библиотеки, такие как EPPlus, CsvHelper + CsvHelper.Excel, терпят неудачу или имеют утечки памяти. Вы ссылаетесь на информацию для метода GetColumnName (почему эти цифры? ...) или метод протестирован правильно? Большое спасибо – Riga 19 May 2017 в 15:51
  • 4
    Я рад, что это хорошо работает для вас @ Рига. Я уверен, что GetColumnName верен, но я не написал кучу тестов для подтверждения. – petelids 19 May 2017 в 16:06
  • 5
    @Riga - 26 - это просто количество букв в алфавите; когда мы дойдем до Z, следующий столбец станет AA, затем из AZ переходим к BA и т. д. 65 - это значение ASCII для A ... У меня нет других ответы, которые объясняют эту логику, но у меня есть ответ, который объясняет это другим способом (например, преобразование ссылки на ячейку в индекс столбца), что может помочь объяснить вещи немного больше. Это можно найти здесь здесь . – petelids 19 May 2017 в 16:28

Excel способен открывать довольно большие файлы, если на вашем компьютере достаточно памяти. Это в большинстве случаев ограничивающий фактор ...

99% библиотек там не были созданы для обработки большого набора данных, и в итоге у вас появятся ошибки в памяти, если вы тоже попытаетесь выбросить много данных у них.

Некоторые из них, например Spout , которые я создал, были созданы для решения этой проблемы. Хитрость заключается в том, чтобы передавать данные и избегать хранения вещей в памяти. Я не уверен, какой язык вы используете (а не PHP), но для вашего языка может быть подобная библиотека. Если нет, вы можете взглянуть на Spout - это open-source - и конвертировать его на ваш язык.

1
ответ дан Adrien 19 August 2018 в 11:28
поделиться
  • 1
    Я согласен с вами в том, что проблема связана с потоковыми данными или указаниями на файл (избегая загрузки полного представления рабочего листа в память). Ваш носик действительно интересен, но, к сожалению, язык, который я использую, - это C #, и портирование было бы слишком экспансивным для меня – Gianluigi Liguori 21 September 2015 в 17:36
  • 2
    @GianluigiLiguori - возможно, есть способ установить PHP и напрямую использовать библиотеку. – miroxlav 21 September 2015 в 18:40
  • 3
    Очевидно, но я ищу родной .NET Solution – Gianluigi Liguori 21 September 2015 в 19:35
  • 4
    Вы пробовали это: microsoft.com/en-us/download/details.aspx?id=5124 ? Он построен Microsoft и, похоже, поддерживает большие электронные таблицы – Adrien 21 September 2015 в 21:33
  • 5
    Да, это вызывает ту же проблему: сам ClosedXML использует OpenXML под капотом – Gianluigi Liguori 22 September 2015 в 14:39

Похоже, вы используете электронную таблицу, где должна использоваться база данных. У этого есть свои ограничения, и это может быть легко одним из них. Читайте дальше только в том случае, если вам абсолютно необходимо придерживаться существующего решения. Однако я не рекомендую его. Потому что есть еще один вопрос: если Excel не может сохранить такой большой файл, сможет ли он открыть такой файл?

Итак, если вы не можете переключиться на платформу базы данных и стандартные библиотеки, о которых вы говорили выше, внутренне неспособный обрабатывать такое количество данных, то, возможно, вы сами по себе при создании большого XLSX. Я имею в виду, например, такой подход:

  1. экспортировать ваши данные партиями (из 1000 или 10000 или любой другой) для разделения файлов для каждой партии
  2. создать инструмент ( (это ближе всего к ), , , , независимо от наличия твердых XML-библиотек), который объединяет отдельные файлы в один. Он включает в себя: извлечение XML из XLSX (обычно file.xlsx\xl\worksheets\sheet1.xml и file.xlsx\xl\worksheets\sharedStrings.xml), склеивая эти части вместе с помощью библиотеки XML-манипулирования (это не должно приводить к сбою OutOfMemoryException, потому что вы больше не работаете со сложными объектами электронных таблиц), переупаковывая файлы результатов обратно на главную XLSX (вы можете взять первый пакетный выходной файл в качестве основного XLSX)

Я показал вам возможный способ достижения результата, но я бы этого избежал. Excel никогда не был платформой для хранения больших объемов данных. По сравнению с вышеуказанной задачей было бы легче убедить руководство в том, что пришло время изменить инструменты / процессы в этой области.

0
ответ дан miroxlav 19 August 2018 в 11:28
поделиться
  • 1
    Я знаю, что использование Excel для хранения такого количества информации - плохая идея. Я уже использовал другой «трюк»: -Export data в CSV ed импортировать его через Microsoft Excel (но он не автоматизирован). Я также оценил решение 3, которое вы мне предоставили, но цель моего сообщения состояла в том, чтобы узнать, были ли использованы эти библиотеки другими способами или если для этой цели существует другая библиотека. Я думаю, что единственным решением является запись исходного документа xlsx после спецификации открытого формата документа и, таким образом, сокращение накладных расходов на объекты. П.С.: Простите за мой английский: D – Gianluigi Liguori 21 September 2015 в 17:47
  • 2
    Похоже, этот ответ - любимая цель для downvotes. – miroxlav 22 September 2016 в 06:50
Другие вопросы по тегам:

Похожие вопросы: