2

有效的..我将处理代码移至 finally 块,现在每次都失败。

我有一个包含 4 条记录、6 列长的测试电子表格。这是我用来引入它的代码。这是 IIS 5(我的电脑)和 IIS 6(Web 服务器)上的 ASP .Net 3.5。

它在捕获前的线上爆炸:“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 工作表 = null;
Excel.Range 范围 = null;

对象 [,] 值 = 空;

尝试
{
    // 配置 Excel
    app = new Excel.ApplicationClass();
    app.Visible = false;
    app.ScreenUpdating = 假;
    app.DisplayAlerts = 假;

    // 用上传的文件打开一个新的excel实例
    book = app.Workbooks.Open(path);

    // 获取书中的第一个工作表
    sheet = (Excel.Worksheet)book.Worksheets[1];

    // 从第二行的第一个单元格开始
    range = sheet.get_Range("A2", Missing.Value);

    // 将所有单元格向右移动
    range = range.get_End(Excel.XlDirection.xlToRight);

    // 向下获取所有单元格
    range = range.get_End(Excel.XlDirection.xlDown);

    // 获取右下角单元格的地址
    字符串 downAddress = range.get_Address(false, false, Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing);

    // 获取完整的数据范围
    range = sheet.get_Range("A2", downAddress);

    // 获取所有数据的二维数组
    值 = (object[,])range.Value2;
}
捕获(异常 e)
{
    LoggingService.log(e.Message);
}
最后
{
    // 清理
    范围=空;
    表=空;

    如果(书!= null)
        book.Close(false, Missing.Value, Missing.Value);

    书=空;

    如果(应用程序!= null)
        app.Quit();

    应用程序=空;
}

返回值;
4

4 回答 4

5

我不确定这是否是您的问题,但很可能是。您没有正确清理您的 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();
于 2009-11-02T19:02:13.260 回答
1

无论您做什么,在每个请求上创建/销毁 excel 都会产生绝对糟糕的性能。一般来说,使用自动化运行任何 Office 应用程序都是一件令人讨厌的事情,原因有很多(请参阅此处)。我让它工作的唯一方法是拥有一个应用程序的单个实例(在我的情况下是 Word),它被初始化一次,然后请求排队到这个实例进行处理

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

于 2009-11-02T19:07:34.950 回答
1

使用 ASP.NET 的互操作会遇到很多麻烦。除非这适用于一些小型的内部应用程序,否则建议不要继续使用它。

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

使用互操作的一些后果是:

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

所有这些都使得常规桌面应用程序需要避免互操作,而对于服务器应用程序只能作为最后的手段使用,因为需要操作或脚本泄漏进程的 GUI 弹出窗口在服务器环境中是谋杀。

一些替代方案包括:

  • 使用基于 Microsoft Office 2007 XML 的格式,以便您可以自己编写 XML 文件。
  • 使用SpreadsheetGear.Net,它是一个 .NET 二进制 Excel 文件读取器/写入器(您不需要安装 Excel,因为它完全独立)。SpreadsheetGear 在 Intertop 接口之后对自身进行建模,以便更轻松地转换旧代码。
于 2009-11-02T19:12:08.977 回答
0

该错误可能正是它所说的,您遇到了内存不足错误。尝试将值数组的加载拆分为几个较小的块,而不是一次获取整个 Range。我在 C# 中尝试了您的代码并且没有任何问题,但是我正在加载的电子表格大部分是空的。

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

您可以尝试使用的一件事是 sheet.UsedRange,这将减少您正在加载的单元格数量。

我学到的一些额外的小东西可能对你有用:

  • 使用 Application 而不是 ApplicationClass
  • 使用 Marshal.FinalReleaseComObject(range) (也适用于工作表、书籍、应用程序),否则您的 EXCEL.EXE 进程将继续存在。
于 2009-11-02T19:09:51.680 回答