0

I need to export a large amount of data to Excel. The data originates from a list of objects. The data varies in size, but it could be up to 7000 columns, and 20,000 rows.

I am using WPF in Visual Studio 2017 on a laptop that has 24 gigs of ram.

The below code generates a system out of memory exception. how can I alter the code below to handle the large data export to excel?

   public static void LargeExportTest(List<ExcelData> arrExport, string sDefaultPath)
    {
        string filename = sDefaultPath + "\\" + Path.GetRandomFileName() + ".xlsx";
        UInt32 sheetId = 1;
        try
        {
            using (SpreadsheetDocument document = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
            {
                //this list of attributes will be used when writing a start element
                List<OpenXmlAttribute> attributes;
                OpenXmlWriter writer;

                document.AddWorkbookPart();
                WorksheetPart workSheetPart = document.WorkbookPart.AddNewPart<WorksheetPart>();

                writer = OpenXmlWriter.Create(workSheetPart);
                writer.WriteStartElement(new DocumentFormat.OpenXml.Spreadsheet.Worksheet());
                writer.WriteStartElement(new DocumentFormat.OpenXml.Spreadsheet.SheetData());

                int batchCount = 0;
                for (int rowNum = 0; rowNum <= arrExport.Count-1; ++rowNum)
                {
                    int u = Convert.ToInt32(rowNum.ToString());

                    batchCount++;
                    //create a new list of attributes
                    attributes = new List<OpenXmlAttribute>();
                    // add the row index attribute to the list
                    attributes.Add(new OpenXmlAttribute("r", null, (rowNum + 1).ToString()));

                    //write the row start element with the row index attribute
                    writer.WriteStartElement(new Row(), attributes);
                    var MaxCol = arrExport[u].ColumnData.Max(x => x.ColumnNumber);

                    attributes = new List<OpenXmlAttribute>();
                    // add data type attribute - in this case inline string (you might want to look at the shared strings table)
                    attributes.Add(new OpenXmlAttribute("t", null, "str"));
                    //add the cell reference attribute
                    attributes.Add(new OpenXmlAttribute("r", "", string.Format(arrExport[u].Name)));

                    //write the cell start element with the type and reference attributes
                    writer.WriteStartElement(new Cell(), attributes);
                    //write the cell value
                    writer.WriteElement(new CellValue(string.Format(arrExport[u].Name)));

                    // write the end cell element
                    writer.WriteEndElement();

                    foreach (var col in arrExport[u].ColumnData)
                    {
                        //reset the list of attributes
                        attributes = new List<OpenXmlAttribute>();
                        // add data type attribute - in this case inline string (you might want to look at the shared strings table)
                        attributes.Add(new OpenXmlAttribute("t", null, "str"));
                        //add the cell reference attribute
                        attributes.Add(new OpenXmlAttribute("r", "", string.Format("{0}{1}", GetColumnName(col.ColumnNumber + 1), (rowNum + 1))));

                        //write the cell start element with the type and reference attributes
                        writer.WriteStartElement(new Cell(), attributes);
                        //write the cell value
                        writer.WriteElement(new CellValue(col.ColumnData.ToString()));

                        // write the end cell element
                        writer.WriteEndElement();
                    }

                    // write the end row element
                    writer.WriteEndElement();

                    if(batchCount > 150)
                    {
                        // write the end SheetData element
                        writer.WriteEndElement();
                        // write the end Worksheet element
                        writer.WriteEndElement();
                      //  writer.Close();



                        //writer.WriteElement(new Sheet()
                        //{
                        //    Name = "Large Sheet" + sheetId.ToString(),
                        //    SheetId = sheetId,
                        //    Id = document.WorkbookPart.GetIdOfPart(workSheetPart)
                        //});
                        sheetId++;

                        //////////////////////////////////////

                        writer = OpenXmlWriter.Create(workSheetPart);
                        writer.WriteStartElement(new DocumentFormat.OpenXml.Spreadsheet.Worksheet());
                        writer.WriteStartElement(new DocumentFormat.OpenXml.Spreadsheet.SheetData());

                        batchCount = 0;
                    }
                }

                // write the end SheetData element
                writer.WriteEndElement();
                // write the end Worksheet element
                writer.WriteEndElement();
                writer.Close();

                writer = OpenXmlWriter.Create(document.WorkbookPart);
                writer.WriteStartElement(new DocumentFormat.OpenXml.Spreadsheet.Workbook());
                writer.WriteStartElement(new DocumentFormat.OpenXml.Spreadsheet.Sheets());

                writer.WriteElement(new Sheet()
                {
                    Name = "Large Sheet" + sheetId.ToString(),
                    SheetId = sheetId,
                    Id = document.WorkbookPart.GetIdOfPart(workSheetPart)
                });

                // End Sheets
                writer.WriteEndElement();
                // End Workbook
                writer.WriteEndElement();

                writer.Close();

                document.Close();
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

I expect an Excel file to be generated without errors, but getting out of memory exception

EDIT: I altered the code in an attempt to create a new worksheet if more than 150 rows have been processed. It is not creating a new worksheet, so the code is not working. New to OpenXml, and not completely sure how to add a new worksheet. Suggestions?

Chris
  • 141
  • 3
  • 13
  • 3
    (totally unrelated tip: use `throw;` instead of `throw ex;`) – Stefan Jun 11 '19 at 18:08
  • Thanks. Using ex to easily see stack trace. Use custom exception handler in production.. – Chris Jun 11 '19 at 18:11
  • [How to properly use OpenXmlWriter to write large Excel files](http://polymathprogrammer.com/2012/08/06/how-to-properly-use-openxmlwriter-to-write-large-excel-files/) – FortyTwo Jun 11 '19 at 21:50
  • @FortyTwo - I believe this code is derived from there anyway via [this answer](https://stackoverflow.com/questions/32690851/export-big-amount-of-data-from-xlsx-outofmemoryexception/32787219#32787219). The code in that answer will support files of this size so I'm not sure what is happening here. What's the size of an `ExcelData` object? One advantage the code in the other answer has is that it's not storing the contents that require writing. Perhaps you could stream the `ExcelData`? – petelids Jun 12 '19 at 11:09
  • My approach now is to modify the code above to create a new worksheet when rows processed is greater than 150. Will edit the code above with what I have now. Being new to Open xml, I am having trouble getting the code to create new worksheets. Any suggestions? – Chris Jun 12 '19 at 12:03
  • Still looking for an answer to this one... Anyone? – Chris Jun 17 '19 at 18:26

0 Answers0