web-dev-qa-db-fra.com

Microsoft.Office.Interop.Excel très lent

J'exporte une matrice 1200 X 800 (indexMatrix) vers un fichier Excel à l'aide de la norme Microsoft.Office.Interop.Excel. L'application fonctionne, mais c'est vraiment très lente (même pour la matrice 100 x 100). J'exporte également dans un fichier texte via TextWriter et cela fonctionne presque instantanément. Est-il possible d'exporter le fichier Excel plus rapidement? 

Voici mon code:

        Excel.Application xlApp=new Excel.Application();
        Excel.Workbook xlWorkBook;
        Excel.Worksheet xlWorkSheet;
        object misValue = System.Reflection.Missing.Value;

        //xlApp = new Excel.ApplicationClass();
        xlWorkBook = xlApp.Workbooks.Add(misValue);

        xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
        for (int i = 0; i < 800; i++)   //h
            for (int j = 0; j < 1200; j++)
                xlWorkSheet.Cells[i+1,j+1] =indexMatrix[i][j];


        xlWorkBook.SaveAs("C:\\a.xls", Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
        xlWorkBook.Close(true, misValue, misValue);
        xlApp.Quit();

        releaseObject(xlWorkSheet);
        releaseObject(xlWorkBook);
        releaseObject(xlApp);

        MessageBox.Show("Excel file created , you can find the file c:\\csharp-Excel.xls");
38
Alex

Vous mettez à jour des cellules individuelles. Cela va être très lent. Si vous y réfléchissez, chaque fois que vous mettez à jour une cellule, un appel RPC est organisé dans le processus Excel.

Il sera beaucoup plus rapide si vous affectez votre tableau de valeurs à deux dimensions à une plage Excel de mêmes dimensions dans une seule instruction (un appel entre processus) à la place de vos 1 200 x 800 = 960 000 appels actuels entre processus. .

Quelque chose comme:

// Get dimensions of the 2-d array
int rowCount = indexMatrix.GetLength(0);
int columnCount = indexMatrix.GetLength(1);
// Get an Excel Range of the same dimensions
Excel.Range range = (Excel.Range) xlWorkSheet.Cells[1,1];
range = range.get_Resize(rowCount, columnCount);
// Assign the 2-d array to the Excel Range
range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, indexMatrix);

En fait, pour être pédant, il existe trois appels inter-processus dans le code ci-dessus (.Cells, .get_Resize et .set_Value), et il y a deux appels par itération dans votre code (.Cells get et un .set_Value implicite) total de 1200 x 800 x 2 = 1 920 000.

Remarque range.get_Resize et range.set_Value étaient nécessaires pour une ancienne version de la bibliothèque d'interopérabilité Excel que j'utilisais au moment de la création de ce message. Ces jours-ci, vous pouvez utiliser range.Resize et range.Value comme indiqué dans le commentaire de @ The1nk.

62
Joe

L'interopérabilité Excel ne sera jamais rapide. Vous contrôlez à distance une instance de l'application Excel. Vous pouvez avoir plus de succès en créant un fichier CSV puis en utilisant Excel interop pour convertir ceci en un fichier .xls ou .xlsx

15
Yuliy

J'ai eu des problèmes similaires lors de la lecture d'un fichier Excel extrêmement volumineux et cela a pris plus de 2 heures avec interop.

J'ai essayé d'utiliser ClosedXml et le processus a pris moins de 10 secondes . ClosedXml

// To loop 
Sheet.Row(y).Cell(x).Value

Gardez également à l'esprit que l'interopérabilité ne fonctionnera pas sur votre serveur si vous n'avez pas installé Excel. ClosedXml n'a pas besoin d'Excel installé.

8
MIKE

Désactivez ScreenUpdating avant d'écrire des données, Application.ScreenUpdating = FALSE, puis activez-le à la fin du code = TRUE

5
Paul Roberts

Utilisez Value2 pour le rendre rapide; Afficher Excel avant de remplir les données

2
Siarhei Kuchuk

ClosedXML est un miracle, il est beaucoup plus rapide et facile à utiliser. 

var workbook = new XLWorkbook();//create the new book

var worksheet = workbook.Worksheets.Add("Computer Install");// Add a sheet
worksheet.Cell(1,1).Value = "PC Name";// (Row, column) write to a cell

workbook.SaveAs(@"LIC documents.xlsx");// Save the book

Vous installez en utilisant un package Get Get. https://www.nuget.org/packages/ClosedXML

2
Chong Ching

Il y a trois façons de le faire, dont 2 sont mentionnées dans différentes réponses par d'autres:

  1. Définissez directement la valeur d'une plage dans Excel sur le tableau 2D.
  2. Écrivez des données dans un fichier CSV, puis utilisez interop pour enregistrer le fichier CSV au format xls ou xlsx.
  3. Écrivez des données dans un fichier CSV, puis utilisez la fonctionnalité de connexions de données pour utiliser le fichier CSV en tant que source de données et importer les données.

Les trois méthodes ci-dessus sont très rapides. Je pourrais écrire des données avec une taille de 90000 lignes et 100 colonnes en environ 6 secondes.

P.S. Ils n'ont toutefois pas résolu mon problème de formatage des données pour les bordures, les styles de police, les couleurs, la fusion de cellules, etc.

0
Sanjit