Почему при работе ASP .NET Excel Interop возникает ошибка «Недостаточно памяти»? - PullRequest
2 голосов
/ 02 ноября 2009

Это было работающим ... и я переместил код утилизации в блок finally, и теперь он каждый раз дает сбой.

У меня есть тестовая таблица с 4 записями, длиной 6 столбцов. Вот код, который я использую для его ввода. Это ASP .Net 3.5 на IIS 5 (мой компьютер) и на IIS 6 (веб-сервер).

Он взрывается в строке прямо перед перехватом: "values ​​= (object [,]) range.Value2;" со следующей ошибкой:

11/2/2009 8:47:43 AM :: Not enough storage is available to complete this operation. (Exception from HRESULT: 0x8007000E (E_OUTOFMEMORY))

Есть идеи? Предложения? Я получил большую часть этого кода от codeproject, поэтому я понятия не имею, правильный ли это способ работы с Excel. Спасибо за любую помощь, вы можете предоставить.

Вот мой код:

Excel.ApplicationClass app = null;
Excel.Workbook book = null;
Excel.Worksheet sheet = null;
Excel.Range range = null;

object[,] values = null;

try
{
    // Configure Excel
    app = new Excel.ApplicationClass();
    app.Visible = false;
    app.ScreenUpdating = false;
    app.DisplayAlerts = false;

    // Open a new instance of excel with the uploaded file
    book = app.Workbooks.Open(path);

    // Get first worksheet in book
    sheet = (Excel.Worksheet)book.Worksheets[1];

    // Start with first cell on second row
    range = sheet.get_Range("A2", Missing.Value);

    // Get all cells to the right
    range = range.get_End(Excel.XlDirection.xlToRight);

    // Get all cells downwards
    range = range.get_End(Excel.XlDirection.xlDown);

    // Get address of bottom rightmost cell
    string downAddress = range.get_Address(false, false, Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing);

    // Get complete range of data
    range = sheet.get_Range("A2", downAddress);

    // get 2d array of all data
    values = (object[,])range.Value2;
}
catch (Exception e)
{
    LoggingService.log(e.Message);
}
finally
{
    // Clean up
    range = null;
    sheet = null;

    if (book != null)
        book.Close(false, Missing.Value, Missing.Value);

    book = null;

    if (app != null)
        app.Quit();

    app = null;
}

return values;

Ответы [ 4 ]

5 голосов
/ 02 ноября 2009

Я не уверен, если это ваша проблема или нет, но это может быть очень хорошо. Вы не очищаете свои объекты Excel должным образом. Они неуправляемый код и их сложно очистить. Наконец, должно выглядеть примерно так: И, как отмечают комментарии, работа с Excel из asp.net не очень хорошая идея. Этот код очистки взят из приложения winform:

 GC.Collect();
 GC.WaitForPendingFinalizers();


 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(range);
 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheet);
 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);

 WB.Close(false, Type.Missing, Type.Missing);

 Excel.Quit();
 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(Excel);

EDIT

Альтернативой может быть использование ado.net для открытия рабочей книги.

            DataTable dt = new DataTable();

            string connectionString;
            System.Data.OleDb.OleDbConnection excelConnection;
            System.Data.OleDb.OleDbDataAdapter da;
            DataTable dbSchema;
            string firstSheetName;
            string strSQL;

            connectionString = @"provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filename + @";Extended Properties=""Excel 12.0;HDR=YES;IMEX=1""";
            excelConnection = new System.Data.OleDb.OleDbConnection(connectionString);
            excelConnection.Open();
            dbSchema = excelConnection.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
            firstSheetName = dbSchema.Rows[0]["TABLE_NAME"].ToString();
            strSQL = "SELECT * FROM [" + firstSheetName + "]";
            da = new OleDbDataAdapter(strSQL, excelConnection);
            da.Fill(dt);

            da.Dispose();
            excelConnection.Close();
            excelConnection.Dispose();
1 голос
/ 02 ноября 2009

Вы столкнетесь с большими трудностями при использовании взаимодействия из ASP.NET. Если это не предназначено для какого-то крошечного домашнего применения, было бы целесообразно не продолжать его.

Office Interop - это не программный API в традиционном смысле этого слова - это макросистема Office, созданная по максимуму, с возможностью работы между процессами - например, макрос Excel может взаимодействовать с Outlook.

Некоторые последствия использования взаимодействия:

  • Вы фактически открываете полную копию офисного приложения.
  • Ваши действия выполняются приложением, как если бы пользователь их инициировал - это означает, что вместо сообщений об ошибках, возвращаемых в коде, они отображаются в графическом интерфейсе.
  • Ваша копия приложения закрывается только в том случае, если вы явно указали это - и даже в этом случае ошибки могут помешать этому на самом деле (приложение может отображать диалоговое окно «Хотите сохранить», если вы программно не сказали Excel, что изменения не нужно спасаться). Обычно это приводит к тому, что многие скрытые копии Excel остаются запущенными в системе - откройте диспетчер задач и посмотрите, сколько запущено процессов Excel.exe.

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

Некоторые альтернативы включают:

  • Использование форматов Microsoft Office 2007 на основе XML, так что вы можете самостоятельно писать файлы XML.
  • Использование SpreadsheetGear.Net , который является программой чтения / записи двоичных файлов Excel в формате .NET (вам не нужно устанавливать Excel, поскольку он полностью автономен). SpreadsheetGear моделирует себя после интерфейсов Intertop, чтобы упростить преобразование старого кода.
1 голос
/ 02 ноября 2009

Создание / уничтожение Excel для каждого запроса будет иметь абсолютно ужасную производительность независимо от того, что вы делаете. В общем, запуск любого приложения Office с использованием автоматизации является неприятным делом по многим причинам (см. здесь ). Единственный способ заставить его работать - это иметь один экземпляр приложения (в моем случае Word), который инициализируется один раз, а затем запросы помещаются в очередь для этого экземпляра для обработки

Если вы можете держаться подальше от приложений и самостоятельно анализировать файл (используя библиотеки MS, просто XML)

0 голосов
/ 02 ноября 2009

Ошибка, вероятно, именно то, что говорится, вы получаете ошибку нехватки памяти. Попробуйте разделить загрузку массива значений на несколько более мелких кусков вместо того, чтобы получать весь Range за один раз. Я опробовал ваш код на C # и у меня не было проблем, но загружаемая мной электронная таблица была в основном пустой.

Я заметил, что Range - это вся таблица (хотя от A2 до IV65536 или что-то в этом роде). Я не уверен, предназначено ли это.

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

Пара дополнительных мелочей, которые я выучил, которые вы можете найти полезными:

  • Использовать приложение вместо ApplicationClass
  • Используйте Marshal.FinalReleaseComObject (range) (а также для листа, книги, приложения), иначе ваш процесс EXCEL.EXE останется без изменений.
...