2009-11-02 38 views
2

working..and我把处理代码移动到finally块,现在它每次都失败。为什么我在使用ASP .NET Excel Interop时出现内存不足错误?

我有一个测试电子表格,有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))

任何想法?建议?我从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; 
+3

从ASP.NET处理Excel需要麻烦。 COM组件不是设计用于多线程环境中的。 – RichardOD 2009-11-02 18:58:38

+0

或任何服务器环境。 Office interop是steriods上的办公宏指令 - 它们*可以*用于可编程访问办公文档,但它不是一种非常可靠的方法。 – David 2009-11-02 19:01:39

+0

够公平的。这可能需要另一个问题,但是如何让用户上传.xls文档从中获取值呢?我不认为这会很难把它变成一个平面文件... – IronicMuffin 2009-11-02 19:03:34

回答

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); 

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(); 
+0

对于使用ADO.NET的建议+1。我会更新你的答案与如何做到这一点OleDb的链接(有很多在那里)。 – RichardOD 2009-11-02 19:07:21

+0

谢谢,我添加了一个简单的示例 – Gratzy 2009-11-02 19:07:59

+0

谢谢,我会给这个镜头。任何避免互操作的事情都会让我开心。我认为提供商与办公室一起安装?我必须确保它们存在于我们的产品盒中。 关于清理,我尝试添加垃圾回收,但没有添加FinalRelease调用。我会先试着看看他们是否完成了这项工作。 – IronicMuffin 2009-11-02 19:20:16

1

创建/在每次请求销毁Excel将有非常糟糕的表现,不管你做什么。一般而言,使用自动化功能运行任何Office应用程序都是非常糟糕的业务,原因很多(请参阅here)。我得到它的工作的唯一办法是让应用程序的单一实例(在我的情况的Word),这是一次初始化,然后请求排队到此实例进行处理

如果你可以留在应用程并自己解析文件(使用MS库,只是XML)

+0

这些请求很少,很远,应用程序在内部,功能是一个额外的...我不介意只要它工作的性能命中。感谢您的文章,这绝对是令人大开眼界......我可能只是抛开互联网,并采用上面的ADO解决方案。 – IronicMuffin 2009-11-02 19:27:19

+0

+1确实,远离Interop和自动化是一个很好的举措。使用Office 2007的更高版本的Windows根本不工作,如果操作系统不适合用于商业应用程序,则不适用。 结帐我的帖子http://stackoverflow.com/questions/1273116/reading-excel-files-as-a-server-process第三方库的摘要为你做这项工作。 – Ian 2009-11-03 10:39:03

0

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

我注意到范围是整个电子表格,但(从A2到IV65536什么的)。我不确定这是否是有意的。

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

一对夫妇的其他小东西,我已经学会了,你可能会发现有用:

  • 使用应用程序,而不是ApplicationClass
  • 使用Marshal.FinalReleaseComObject(范围)(也用于纸张,书籍,应用程序)否则你会让你的EXCEL.EXE进程陷入困境。
+0

对于OOM错误的公平性......我只是觉得很难相信,因为实际上只有24个单元格被导入......绝对不是我打算引入所有65536行和列......看起来像我的错误就在那里。谢谢。 – IronicMuffin 2009-11-02 19:24:59

1

使用ASP.NET中的interop会遇到很多麻烦。除非这是为了一些微小的内部应用,否则建议不要继续使用它。

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接口之后自我模型,使旧代码的转换更容易。
+0

我知道它会打开应用程序并按照提及的进行处理。这是一个很小的内部应用程序,所以效率并不是真正的目标。我只需要添加该功能,以便他们可以根据需要使用它。我很乐意使用'07,但是我们在'00'卡住了。我会看看你提供的链接。谢谢。 – IronicMuffin 2009-11-02 19:23:33

相关问题