.net - XLSX-OutOfMemoryException से बड़ी मात्रा में डेटा निर्यात करें




openxml closedxml (2)

मैं एक्सेल ओपनएक्सएमएल फॉर्मेट (xlsx) में बड़ी मात्रा में डेटा (115.000 पंक्तियां x 30 कॉलुमैंड) निर्यात करने के लिए आ रहा हूं। मैं DocumentFormat.OpenXML, ClosedXML, NPOI जैसे कुछ पुस्तकालयों का उपयोग कर रहा हूं।

इनमें से प्रत्येक के साथ, OutOfMemoryException को फेंक दिया जाता है क्योंकि मेमोरी में शीट का प्रतिनिधित्व एक घातीय स्मृति वृद्धि का कारण बनता है।

दस्तावेज़ फ़ाइल को हर 1000 बार (और मेमोरी रिलीज़ करना) बंद करना, अगले लोडिंग के कारण मेमोरी बढ़ जाती है।

क्या मेमोरी में बहुत अधिक कब्जे के बिना xlsx में डेटा निर्यात करने का एक अधिक निष्पादन तरीका है?


एक्सेल बहुत बड़ी फ़ाइलों को खोलने में सक्षम है, जब तक आपके कंप्यूटर में पर्याप्त मेमोरी है। उस समय सीमित कारक है ...

99% लाइब्रेरियों का निर्माण बड़े डेटा सेट को संभालने के लिए नहीं किया गया है और यदि आप उन पर बहुत अधिक डेटा फेंकने का प्रयास करते हैं, तो आप मेमोरी त्रुटियों से बाहर निकल जाएंगे।

उनमें से कुछ, जैसे कि Spout जो मैंने बनाई थी, इस समस्या को हल करने के लिए बनाई गई है। ट्रिक डेटा को स्ट्रीम करने और मेमोरी में चीजों को स्टोर करने से बचने के लिए है। मुझे यकीन नहीं है कि आप किस भाषा का उपयोग कर रहे हैं (PHP ऐसा नहीं लगता है), लेकिन आपकी भाषा के लिए एक समान पुस्तकालय हो सकता है। यदि नहीं, तो आप अभी भी टोंटी को देख सकते हैं - यह ओपन-सोर्स है - और इसे अपनी भाषा में रूपांतरित करें।


ओपनएक्सएमएल एसडीके इस काम के लिए सही उपकरण है, लेकिन आपको DOM दृष्टिकोण के बजाय SAX (सिंपल एपीआई फॉर एक्सएमएल) दृष्टिकोण का उपयोग करने के लिए सावधान रहने की आवश्यकता है। SAX के लिए लिंक किए गए विकिपीडिया लेख से:

जहाँ DOM संपूर्ण रूप से दस्तावेज़ पर कार्य करता है, SAX पार्सर XML दस्तावेज़ के प्रत्येक टुकड़े पर क्रमिक रूप से कार्य करता है

यह बड़ी Excel फ़ाइलों को संभालते समय खपत की गई मेमोरी की मात्रा को बहुत कम कर देता है।

यहाँ पर एक अच्छा लेख है - http://polymathprogrammer.com/2012/08/06/how-to-properly-use-openxmlwriter-to-write-large-excel-files/

उस लेख से अनुकूलित, यहाँ एक उदाहरण है जो 30 कॉलम के साथ 115k पंक्तियों को आउटपुट करता है:

public static void LargeExport(string filename)
{
    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 Worksheet());
        writer.WriteStartElement(new SheetData());

        for (int rowNum = 1; rowNum <= 115000; ++rowNum)
        {
            //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.ToString()));

            //write the row start element with the row index attribute
            writer.WriteStartElement(new Row(), attributes);

            for (int columnNum = 1; columnNum <= 30; ++columnNum)
            {
                //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(columnNum), rowNum)));

                //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("This is Row {0}, Cell {1}", rowNum, columnNum)));

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

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

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

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

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

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

        writer.Close();

        document.Close();
    }
}

//A simple helper to get the column name from the column index. This is not well tested!
private static string GetColumnName(int columnIndex)
{
    int dividend = columnIndex;
    string columnName = String.Empty;
    int modifier;

    while (dividend > 0)
    {
        modifier = (dividend - 1) % 26;
        columnName = Convert.ToChar(65 + modifier).ToString() + columnName;
        dividend = (int)((dividend - modifier) / 26);
    }

    return columnName;
}