Потоковая передача данных Excel с помощью Open XML SDK - PullRequest
0 голосов
/ 01 июня 2018

У нас есть книга Excel, которая довольно большая.Около 3300 столбцов и несколько тысяч строк.

Мы обнаружили, что попытка что-либо сделать с данными приводит к высокому использованию памяти, около 3 ГБ.

Похоже, что пакет DocumentFormat.OpenXmlпри повторении сохраняет полную структуру объекта рабочего листа в памяти.Как правило, мы делаем это:

var workbookPart = _document.WorkbookPart;
var worksheets = workbookPart.Workbook.Descendants<Sheet>();

foreach(var worksheet in worksheets)
{
    var worksheetPart = (WorksheetPart) workbookPart.GetPartById(worksheet.Id);
    foreach(var row in worksheetPart.Worksheet.Descendants<Row>())
    {
        foreach(var cell in row.Descendants<Cell>())
        {
            var (_, value) = ParseCell(cell);
        }
    }
}

ParseCell просто получает содержимое Cell, просматривая строковое значение из SharedStringTable в книге или, если это число,парсинг числа.

Простой запуск этого кода, который ничего не делает с результатом ParseCell, все еще использует значительную память.

Когда мы профилировали этот код, мы заметили, что существует Cellв куче для каждой ячейки на листе, несмотря на все наши попытки использовать API IEnumerable<T>, чтобы исключить наличие больших коллекций в памяти.

Это довольно близко к рекомендованному использованию этого пакета Nuget.

Из профилирования появляется проблема в том, что каждый Cell имеет сильную ссылку на следующий Cell, и аналогично для Row.

Каждый Cell имеетполе с именем и _next, которое удерживает каждую ячейку с сильным корнем.Ячейка A имеет сильную ссылку на ячейку B, B на C, C на D.

Row имеет аналогичную структуру, где строка 0 имеет поле _next для строки 1, и так далее, и так далееТаким образом, для каждого Row, через который мы проходим, он строго ссылается на следующий Row.

Так что все связаны друг с другом.Когда я посмотрел на это с помощью WinDbg после того, как он обработал последние 10 * 10, в куче было ровно столько Cell с от !dumpheap -stat, сколько содержится в книге.

То, как мы используемэтот SDK не будет масштабироваться до большего количества строк.Есть ли способ более эффективно использовать этот пакет и обрабатывать рабочую таблицу построчно, не сохраняя в памяти граф объектов всего рабочего листа?

1 Ответ

0 голосов
/ 01 июня 2018

Подходящим решением здесь является использование OpenXmlReader XML reader.Другой ключевой момент - использовать Elements вместо Decendents, чтобы не заглядывать слишком глубоко в структуру XML.

using (var reader = OpenXmlReader.Create(worksheetPart))
{
    while (reader.Read())
    {
        if (typeof(Row).IsAssignableFrom(reader.ElementType))
        {
            var row = (Row)reader.LoadCurrentElement();
            foreach (var cell in row.Elements<Cell>())
            {
                var (_, value) = ParseCell(cell);
            }
        }
    }
}

Это действительно «поток» элементов и использование памяти минимально.

...