为什么我得到一个内存不足错误做ASP .NET Excel互操作?
这是工作 ..我把处理代码移动到finally块,现在每次都失败。
我有一个testing电子表格,有4个logging,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))
有任何想法吗? build议? 我从代码项目中得到了大部分代码,所以我不知道这是否是使用Excel的正确方法。 感谢您的任何帮助,您可以提供。
这是我的代码:
Excel.ApplicationClass app = null; Excel.Workbook book = null; Excel.Worksheet sheet = null; Excel.Range range = null; object [,] values = null; 尝试 { //configurationExcel app = new Excel.ApplicationClass(); app.Visible = false; app.ScreenUpdating = false; app.DisplayAlerts = false; //用上传的文件打开一个新的excel实例 book = app.Workbooks.Open(path); //获取书中的第一张工作表 sheet =(Excel.Worksheet)book.Worksheets [1]; //从第二行的第一个单元格开始 范围= sheet.get_Range(“A2”,Missing.Value); //获取所有单元格 range = range.get_End(Excel.XlDirection.xlToRight); //获取所有单元格向下 range = range.get_End(Excel.XlDirection.xlDown); //获取最右下angular的单元格的地址 string downAddress = range.get_Address(false,false,Excel.XlReferenceStyle.xlA1,Type.Missing,Type.Missing); //获取完整的数据范围 range = sheet.get_Range(“A2”,downAddress); //获取所有数据的2d数组 values =(object [,])range.Value2; } 捕获(例外e) { LoggingService.log(e.Message); } 最后 { // 清理 range = null; sheet = null; 如果(book!= null) closures(false,Missing.Value,Missing.Value); book = null; 如果(app!= null) app.Quit(); app = null; } 返回值;
我不确定这是不是你的问题,但很可能。 你没有正确地清理你的Excel对象。 他们是非托pipe的代码,可能会很棘手的清理。 最后应该看起来像这样:而且正如注释已经注意到从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();
创build/销毁Excel的每一个请求将有绝对可怕的performance,不pipe你做什么。 一般来说运行任何使用自动化的Office应用程序是一个令人讨厌的业务,原因很多(请看这里 )。 我得到它的唯一方法是有一个应用程序的单个实例(在我的情况下,Word)初始化一次,然后请求排队到这个实例进行处理
如果您可以远离应用程序并自行parsing文件(使用MS库,仅使用XML)
使用ASP.NET的interop会遇到很多麻烦。 除非这意味着一些小的内部应用程序,否则build议不要继续使用它。
Office Interop不是传统意义上的编程API,而是Officemacros系统,它具有处理进程的能力 – 例如,Excelmacros可以与Outlook进行交互。
使用interop的一些后果是:
- 您实际上正在打开办公室应用程序的完整副本。
- 您的操作正在由应用程序执行,就像用户启动它们一样 – 这意味着代替返回错误消息,它们将显示在GUI中。
- 您的应用程序副本只有在您明确命令的情况下才会closures – 即使这样,错误也可以防止实际发生(如果您没有可编程地告诉Excel这些更改不需要,应用程序可能会显示“是否要保存”对话框被保存)。 这通常会导致许多隐藏的Excel副本在系统上运行 – 打开任务pipe理器并查看有多less个excel.exe进程正在运行。
所有这些使得互操作性避免了常规的桌面应用程序,以及只能作为服务器应用程序的最后手段的东西,因为需要操作或脚本泄露进程的GUIpopup窗口在服务器环境中是谋杀的。
一些替代品包括:
- 使用基于Microsoft Office 2007 XML的格式,以便您可以自己编写XML文件。
- 使用SpreadsheetGear.Net ,这是一个.NET二进制Excel文件读取器/写入器(不需要安装Excel,因为它是完全独立的)。 SpreadsheetGear在Intertop接口之后自行build模,使旧代码的转换更容易。
该错误可能正是它所说的,你正在得到一个内存不足的错误。 尝试将值数组的加载拆分成几个较小的块,而不是一次获取整个范围。 我在C#中试过你的代码,没有问题,但我加载的电子表格大多是空的。
我注意到范围是整个电子表格,但(从A2到IV65536什么的)。 我不确定这是否是有意的。
有一件事你可以尝试使用sheet.UsedRange,这将减less你正在加载的单元格的数量。
一些额外的小东西,我已经了解到你可能会觉得有用:
- 使用Application而不是ApplicationClass
- 使用Marshal.FinalReleaseComObject(范围)(也适用于工作表,书籍,应用程序),否则你会有你的EXCEL.EXE进程粘附。