Pourquoi est-ce que je reçois une erreur de mémoire insuffisante lors de l'interopérabilité ASP .NET Excel ?

StackOverflow https://stackoverflow.com/questions/1662982

Question

Ce était fonctionne..et j'ai déplacé le code d'élimination vers le bloc final, et maintenant il échoue à chaque fois.

J'ai une feuille de calcul de test avec 4 enregistrements de 6 colonnes.Voici le code que j'utilise pour l'introduire.Il s'agit d'ASP .Net 3.5 sur IIS 5 (mon PC) et sur IIS 6 (serveur web).

Il explose sur la ligne juste avant la capture :"valeurs = (objet [,]) range.value2;" avec l'erreur suivante:

11/2/2009 8:47:43 AM :: Not enough storage is available to complete this operation. (Exception from HRESULT: 0x8007000E (E_OUTOFMEMORY))

Des idées?Suggestions?J'ai récupéré la majeure partie de ce code sur codeproject, donc je ne sais pas si c'est la bonne façon de travailler avec Excel.Merci pour toute l'aide que vous pourrez fournir.

Voici mon code :

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;
Était-ce utile?

La solution

Je ne sais pas si c'est votre problème ou non, mais cela pourrait très bien l'être.Vous ne nettoyez pas correctement vos objets Excel.Il s’agit de code non géré et peut être difficile à nettoyer.Finalement, cela devrait ressembler à ceci :Et comme les commentaires l'ont noté, travailler avec Excel depuis asp.net n'est pas une bonne idée.Ce code de nettoyage provient d'une application 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);

MODIFIER

Une alternative serait d'utiliser ado.net pour ouvrir le classeur.

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

Autres conseils

Créer/détruire Excel à chaque requête aura des performances absolument terribles, peu importe ce que vous faites.En général, exécuter n’importe quelle application Office à l’aide de l’automatisation est une mauvaise affaire pour de nombreuses raisons (voir ici).La seule façon de le faire fonctionner est d'avoir une seule instance de l'application (dans mon cas, Word) qui est initialisée une fois, puis les demandes sont mises en file d'attente vers cette instance pour traitement.

Si vous pouvez rester à l'écart des applications et analyser le fichier vous-même (en utilisant les bibliothèques MS, uniquement XML)

Vous allez rencontrer beaucoup de problèmes en utilisant l'interopérabilité d'ASP.NET.À moins que cela ne soit destiné à une petite application interne, il serait conseillé de ne pas aller de l'avant.

Office Interop n'est pas une API de programmation au sens traditionnel du terme - c'est le système de macros Office poussé à son maximum, avec la possibilité de travailler entre processus - par exemple une macro Excel pourrait interagir avec Outlook.

Certaines conséquences de l’utilisation de l’interopérabilité sont :

  • Vous ouvrez en fait une copie complète de l’application Office.
  • Vos actions sont exécutées par l'application comme si un utilisateur les avait initiées - cela signifie qu'au lieu que des messages d'erreur soient renvoyés dans le code, ils sont affichés dans l'interface graphique.
  • Votre copie de l'application ne se ferme que si vous la commandez explicitement - et même dans ce cas, des erreurs peuvent empêcher que cela se produise (l'application peut présenter une boîte de dialogue "Voulez-vous enregistrer" si vous n'avez pas indiqué par programmation à Excel que les modifications n'ont pas besoin d'être effectuées). à sauvegarder).Cela entraîne généralement l'exécution de nombreuses copies cachées d'Excel sur le système - ouvrez le gestionnaire de tâches et voyez combien de processus Excel.exe sont en cours d'exécution.

Tout cela fait de l'interopérabilité quelque chose à éviter pour les applications de bureau classiques, et quelque chose qui ne devrait être utilisé qu'en dernier recours pour les applications serveur, car une fenêtre contextuelle d'interface graphique nécessitant une action ou un script qui fuit le processus est un meurtre dans un environnement serveur.

Certaines alternatives incluent :

  • Utilisation des formats basés sur Microsoft Office 2007 XML, afin que vous puissiez écrire les fichiers XML vous-même.
  • En utilisant Feuille de calculGear.Net, qui est un lecteur/enregistreur de fichiers Excel binaires .NET (vous n'avez pas besoin d'Excel installé, car il est complètement autonome).SpreadsheetGear se modélise d'après les interfaces Intertop pour faciliter la conversion d'anciens codes.

L'erreur est probablement exactement ce qu'elle indique, vous obtenez une erreur de mémoire insuffisante.Essayez de diviser le chargement du tableau de valeurs en plusieurs morceaux plus petits au lieu d'obtenir la totalité de la plage en même temps.J'ai essayé votre code en C# et je n'ai eu aucun problème, mais ma feuille de calcul que je chargeais était en grande partie vide.

J'ai remarqué que la plage correspondait à la feuille de calcul entière (de A2 à IV65536 ou quelque chose du genre).Je ne sais pas si c'est intentionnel.

Une chose que vous pouvez essayer d'utiliser est sheet.UsedRange, qui réduira le nombre de cellules que vous chargez.

Quelques petites choses supplémentaires que j'ai apprises et qui pourraient vous être utiles :

  • Utiliser Application au lieu de ApplicationClass
  • Utilisez Marshal.FinalReleaseComObject(range) (et également pour la feuille, le livre, l'application), sinon votre processus EXCEL.EXE restera en place.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top