2009-11-02 41 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

相關問題