OutOfMemoryException при сохранении/создании/экспорте листа Excel

Среда разработки:

  • ОС - Windows 7 64bit
  • Процессор - i5 460M
  • Оперативная память - 8 ГБ
  • Платформа .NET — 4.0
  • Excel-Interop — библиотека объектов Microsoft Excel 14.0

Я использую Excel-Interop для экспорта файла Excel из DataGridView (dgv).

Когда я сохраняю более 150 000 строк

OutOfMemoryException

бросается.

{
    object[,] valueObjArray = new object[rowCnt, colCnt];
    int rowCnt = dgv.Rows.Count;
    int colCnt = dgv.Columns.Count;

    for (int rowIndex = 0; rowIndex < rowCnt; rowIndex++)
    {
        for (int colIndex = 0; colIndex < colCnt; colIndex++)
        {
            valueObjArray[rowIndex, colIndex] = dgv[colIndex, rowIndex].Value;
        }
    }

    _workSheet.get_Range("A1", Convert.ToChar(colCnt + 64).ToString() + "1").Value2 = headerObjArray;                
    _workSheet.get_Range("A2", Convert.ToChar(colCnt + 64).ToString() + (rowCnt + 1).ToString()).Value2 = valueObjArray;
    _workSheet.get_Range("B2", "B" + (rowCnt+1).ToString()).NumberFormat = "yyyy-mm-dd hh:mm";

    _workBook.SaveAs(path);
}

Это лучший способ ускорить, что я знаю.

Но после мониторинга оперативной памяти я думаю, что это вызывает увеличение памяти. Исключение возникает, когда использование памяти достигает около 900 МБ.

Как поймать это исключение?


person Tony Jang    schedule 29.02.2016    source источник
comment
@Trix Спасибо за ваше редактирование: D   -  person Tony Jang    schedule 29.02.2016
comment
Наконец, я сохранил 258927 строк с 15 столбцами. Размер экспортируемого файла составляет 24,2 МБ, а использование памяти составляет около 600 МБ.   -  person Tony Jang    schedule 29.02.2016
comment
В вопросе вы написали, что у вас проблема со 150 00 строк. Это меньше, чем 258 927.   -  person Michał Komorowski    schedule 29.02.2016
comment
В какой строке вы получили OutOfMemoryException? В строке, где создается valueObjArray или когда вызывается метод SaveAs...   -  person Michał Komorowski    schedule 29.02.2016
comment
@MichałKomorowski _workSheet.get_Range(A2, Convert.ToChar(colCnt + 64).ToString() + (rowCnt + 1).ToString()).Value2 = valueObjArray; Эта строка получена. И 258 927 строк выполнены успешно, но 270 000 строк не выполнены.   -  person Tony Jang    schedule 29.02.2016


Ответы (1)


Попробуйте сделать это партиями:

//We will call SaveAs method many times and we don't want to be asked
//if a file should be overwritten every time.
xlApp.DisplayAlerts = false

int rowCnt = dgv.Rows.Count;
int colCnt = dgv.Columns.Count;

int batchSize = 100000; //Try to experiment with other values
int currentRow = 0;

object[,] valueObjArray = new object[batchSize, colCnt];

_workSheet.get_Range("A1", Convert.ToChar(colCnt + 64).ToString() + "1").Value2 = headerObjArray;     

while (currentRow < rowCnt)
{
    for (int rowIndex = 0; rowIndex < batchSize && currentRow + rowIndex < rowCnt; rowIndex++)
    {
        for (int colIndex = 0; colIndex < colCnt; colIndex++)
        {
            valueObjArray[rowIndex, colIndex] =             
            dgv[colIndex, currentRow + rowIndex].Value;
        }
    }

    ws.get_Range("A2", Convert.ToChar(colCnt + 64).ToString() + (currentRow + batchSize + 1).ToString()).Value2 = valueObjArray;
    ws.get_Range("B2", "B" + (currentRow + batchSize + 1).ToString()).NumberFormat = "yyyy-mm-dd hh:mm";

    wb.SaveAs("a.xlsx");

    currentRow += batchSize;
}

Таким образом мне удалось сохранить 1 миллион строк. Я протестировал его с поддельными данными, поэтому могут потребоваться некоторые незначительные изменения / исправления.

person Michał Komorowski    schedule 29.02.2016
comment
Спасибо за Ваш ответ! Это работает нормально. Но то, что я должен использовать пакет, означает, что есть предел емкости. Я знаю, где предел? - person Tony Jang; 29.02.2016
comment
Ну, трудно указать точный предел, это зависит от количества столбцов, размера данных в каждой ячейке, технических характеристик машины... - person Michał Komorowski; 29.02.2016
comment
Это решение выглядит очень многообещающе :) Всего один вопрос. При переходе к следующему пакету, я думаю, вам нужно будет изменить следующую строку: ws.get_Range("A2", Convert.ToChar(colCnt + 64).ToString() + (currentRow + batchSize + 1).ToString()).Value2 = valueObjArray; Особенно A2 с динамическим значением, которое учитывает значение пакета. - person Ivaylo; 15.01.2021