代码之家  ›  专栏  ›  技术社区  ›  IronicMuffin

为什么在执行ASP.NET Excel互操作时出现内存不足错误?

  •  2
  • IronicMuffin  · 技术社区  · 15 年前

    这个 我把处理代码移到最后一个块,现在每次都失败了。

    我有一个测试电子表格,有4个记录,6列长。这是我用来输入的代码。这是IIS 5(我的电脑)和IIS 6(Web服务器)上的ASP.NET 3.5。

    它在catch前面的一行放大:“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))

    有什么想法吗?建议?我从代码项目中获得了大部分代码,所以我不知道这是否是使用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 回复  |  直到 15 年前
        1
  •  5
  •   Gratzy    15 年前

    我不确定这是否是你的问题,但很可能是。您没有正确清理Excel对象。它们是非托管代码,清理起来很困难。最后应该是这样的:正如评论所指出的,使用ASP.NET中的Excel不是一个好主意。此清理代码来自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);
    

    编辑

    另一种选择是使用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();
    
        2
  •  1
  •   mfeingold    15 年前

    在每个请求上创建/销毁Excel都将具有绝对糟糕的性能,无论您做什么。一般来说,运行任何使用自动化的办公应用程序都是一件令人讨厌的事情,原因有很多(参见 here )我让它工作的唯一方法是让应用程序的单个实例(在我的例子中是Word)初始化一次,然后将请求排队到该实例进行处理。

    如果您可以远离应用程序并自己解析文件(使用MS库,仅XML)

        3
  •  1
  •   David    15 年前

    从ASP.NET使用interop会遇到很多问题。除非这是为了一些小的内部应用,否则最好不要继续使用它。

    Office Interop不是传统意义上的编程API——它是Office宏系统发挥到了极致,具有处理过程间工作的能力——例如,Excel宏可以与Outlook交互。

    使用interop的一些后果是:

    • 您实际上正在打开Office应用程序的完整副本。
    • 应用程序正在执行您的操作,就好像用户启动了它们一样——这意味着,它们不会以代码的形式返回错误消息,而是显示在GUI中。
    • 只有当您显式地命令应用程序时,应用程序的副本才会关闭,即使这样,错误也会阻止它的实际发生(如果您没有通过编程方式告诉Excel不需要保存更改,应用程序可能会显示一个“是否要保存”对话框)。这通常会导致许多隐藏的Excel副本留在系统-打开任务管理器上运行,并查看有多少Excel.exe进程正在运行。

    所有这一切都使得Interop成为常规桌面应用程序所要避免的,并且只能作为服务器应用程序的最后手段,因为需要操作或脚本的GUI弹出窗口泄漏过程在服务器环境中是谋杀。

    一些替代方案包括:

    • 使用基于XML的MicrosoftOffice2007格式,这样您就可以自己编写XML文件。
    • 使用 SpreadsheetGear.Net ,这是一个.NET二进制Excel文件读写器(您不需要安装Excel,因为它完全独立)。电子表格装置在Intertop接口之后对自身进行建模,以简化旧代码的转换。
        4
  •  0
  •   David Dietrich    15 年前

    这个错误可能正是它所说的,您得到了一个内存不足的错误。尝试将值数组的加载拆分为几个较小的块,而不是一次获取整个范围。我在C中试用了您的代码,没有问题,但我加载的电子表格大多是空的。

    我注意到范围是整个电子表格(从A2到IV65536或其他)。我不确定这是不是有意的。

    你可以尝试使用sheet.usedRange,它可以减少你正在加载的单元格的数量。

    我学到了一些你可能会觉得有用的小东西:

    • 使用应用程序而不是ApplicationClass
    • 使用marshal.finalReleaseComObject(range)(也适用于sheet、book、app),否则您的excel.exe进程会一直存在。