Работа с большими файлами экселя

в 20:26, , рубрики: Excel, метки: ,

Что такое большой файл? Ну так чтобы реально большой? В бытность свою я думал, что это файлик на 50-60 тыс строк записей. И оставался я бы в таком неведении до сих пор, но пришлось выполнять один проект, в котором надо было работать с файлами на 600-800 тыс строк кода. Хождение по мукам — под катом:

Что сначала

А сначала, друзья мои, ринулись мы в самое простое, что можно придумать. Interop.Excell, и все дела. Казалось. Ага, щаз. Как показали тестовые испытания, данный способ открытия приводил к тому, что за час было прочитано 200 тыс строк экселя, приложение активно потребляло оперативку, и раздвигало плечами остальные процессы на машине. Кончилось все ожидаемо, но следственный эксперимент надо было довести до конца — на 260 тысячах приложение свалилось в OutOfMemory на машине с 4 Гб. Стало понятно, что в лоб решить проблему не получится

Google it

Сколько нам открытий чудных… Гугль привел, как ни странно, в msdn, где я познакомился с двумя методами открытия очень больших файлов: DOM и SAX. Уж за давностью времен не вспомню, но какой то из них отвалился по причине опостылевшей уже на тот момент OutOfMemory, а второй был совершенно неюзабелен в плане доступа к данным. Почему — читаем ниже.

Из чего же, из чего же

Сделаны наши эксельки. Ни для кого, кто решил копнуть формат чуть глубже, не станет секретом, что в отличие от бинарным xls, xlsx — по сути rar архив с данными. Достаточно поменять расширение ручками и распаковать архив в папку — и мы получим всю внутреннюю структуру документа, что есть не что иное, как набор xml файлов и сопутствующей информации. Как оказалось, в корневом xml нет текстовых данных. Вместо этого мы имеем набор индексов, которые ссылаются на вспомогательный файл, в котором представлены пары «ключ/значение» Одним из вышеприведенных способов открыть то файл можно, но при этом нужно копаться в сопутствующих файлах и вытаскивать из них текстовые значения. Мрак.

И отступила тьма

После долгих мытарств и стенаний родилось следующее:

Наши любимые юзинги, которые некоторые личности забывают указывать:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

Собственно, сам код:
public delegate void MessageHave(string message);

public delegate void _DataLoaded(List data);

public delegate void _NewProcent(int col);

public static _DataLoaded DataLoaded;

public static _NewProcent NewProcent;

public static MessageHave MessageHave_Event;

public static void ReadData(object data)
{
//Приводим объект с переданной парой "имя файла"-"выбранный лист экселя"
var keyValuePair = (KeyValuePair<string, string>)data;
using (var cnn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" +
keyValuePair.Key + @";Extended Properties=""Excel 12.0;HDR=No;IMEX=1""")
)
{
int calc = 1000;
MessageHave_Event("Открытие соединения провайдера");
cnn.Open();
try
{
var cmd = new OleDbCommand(String.Format("select * from [{0}]", keyValuePair.Value), cnn);
using (OleDbDataReader dr = cmd.ExecuteReader())
{
var lines = new List();
int id = 0;
if (dr != null)
while (dr.Read())
{
string text = "";
for (int i = 0; i < dr.FieldCount; ++i)
{
if (dr[i] != null)
text += dr[i] + "^";//добавляем разделитель между ячейками
else
text += "^";
}
lines.Add(text);

id++;
if (id == calc)
{
NewProcent(id);
calc += 1000;
}
}
DataLoaded(lines);
}
cnn.Close();
}
catch (Exception ex)
{
MessageHave_Event("Exception: " + ex.Message);
cnn.Close();
}
}
}

Код показал производительность порядка 15-20 минут на файлах в 600-800 тыс строк записей.

Если кому то реализация покажется кривой - сильно не пинать :) Выслушаю все комментарии

Автор: dtcDev

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js