当我编写相当大的xlsx文件时,我按照此页面上的建议使用OpenXmlReaderOpenXmlWriter

https://blogs.msdn.microsoft.com/brian_jones/2010/06/22/writing-large-excel-files-with-the-open-xml-sdk/

我唯一要做的就是更改现有单元格中的公式,并确保丢弃它们的值,以便在Excel打开文件时重新计算它。

这是我正在使用的功能:

    public void Save(Stream Input, Stream Output)
    {
        Input.Position = 0;
        if (Input != Output)
            Input.CopyTo(Output);

        using (SpreadsheetDocument document = SpreadsheetDocument.Open(Output, true))
        {
            WorkbookPart wbPart = document.WorkbookPart;

            // force recalculation as we change formulas
            wbPart.Workbook.CalculationProperties.ForceFullCalculation = true;
            wbPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;

            // store the worksheet parts in a separate list because the loop below
            // adds and removes elements inside wbPart.WorksheetParts
            List<WorksheetPart> originalWsParts = new List<WorksheetPart>();
            foreach (WorksheetPart inputWsPart in wbPart.WorksheetParts)
                originalWsParts.Add(inputWsPart);

            // process all worksheets in the workbook
            foreach (WorksheetPart inputWsPart in originalWsParts)
            {
                string origninalSheetId = wbPart.GetIdOfPart(inputWsPart);

                WorksheetPart replacementWsPart = wbPart.AddNewPart<WorksheetPart>();
                string replacementWsPartId = wbPart.GetIdOfPart(replacementWsPart);

                OpenXmlReader reader = OpenXmlReader.Create(inputWsPart);
                OpenXmlWriter writer = OpenXmlWriter.Create(replacementWsPart);

                while (reader.Read())
                {
                    logger.Debug(reader.ElementType.Name);
                    if (reader.ElementType == typeof(Cell) && reader.IsStartElement)
                    {
                        writer.WriteStartElement(reader);

                        // write the cell content, changing the formula and skipping the value
                        while (reader.Read() && !(reader.ElementType == typeof(Cell) && reader.IsEndElement))
                        {
                            if (reader.IsStartElement)
                            {
                                if (reader.ElementType == typeof(CellFormula))
                                {
                                    CellFormula element = reader.LoadCurrentElement() as CellFormula;
                                    element.Text = "SUM(1,2)";
                                    element.CalculateCell = true;
                                    writer.WriteElement(element);
                                }
                                else if (reader.ElementType != typeof(CellValue))
                                {
                                    writer.WriteStartElement(reader);
                                    string elementText = reader.GetText();
                                    if (!String.IsNullOrEmpty(elementText))
                                        writer.WriteString(elementText);
                                }
                            }
                            else if (reader.IsEndElement)
                            {
                                if (reader.ElementType != typeof(CellValue))
                                    writer.WriteEndElement();
                            }
                        }

                        writer.WriteEndElement();
                    }
                    else
                    {
                        if (reader.IsStartElement)
                        {
                            writer.WriteStartElement(reader);
                            string elementText = reader.GetText();
                            if (!String.IsNullOrEmpty(elementText))
                                writer.WriteString(elementText);
                        }
                        else if (reader.IsEndElement)
                        {
                            writer.WriteEndElement();
                        }
                    }
                }

                reader.Close();
                writer.Close();

                Sheet sheet = wbPart.Workbook.Descendants<Sheet>()
                    .Where(s => s.Id.Value.Equals(origninalSheetId)).First();
                sheet.Id.Value = replacementWsPartId;
                wbPart.DeletePart(inputWsPart);
            }
        }
    }


它可以在最简单的工作簿上很好地工作,但是当文件中的图纸内有图纸时,它会创建不可读的文件。
例如,如果我在Sheet1上有一个图纸,当Excel打开保存的文件时,它会抱怨该文件包含不可读的部分,并在已删除的事物列表中向我显示图纸。

我解压缩了xlsx文件,并比较了sheetX.xml文件,除了添加的x:前缀之外,它们是相同的。

显然,我缺少了一些东西,但是阅读了我能找到的各种文档后,什么也没想到。我相信有对原始工作表零件的引用,该零件尚未更新,但在工作簿中看不到任何工程图。

任何帮助都是最欢迎的。

更新资料

我仔细查看了文件内容,xl文件夹中缺少两个文件夹:chartsdrawings

显然,我缺少将这些代码添加到最终存档中的代码,但是我(尚未)弄清楚这是什么代码。

最佳答案

当总是要克隆输入部分时,以上代码在循环内创建了一个新的WorksheetPart

因此,我去寻找一种克隆工作表的方法,但是ClonePart()上没有WorkbookPart方法。

对我来说幸运的是,其他人已经遇到了这个问题,布莱恩·琼斯(Brian Jones)在他的博客上做了以下工作:

https://blogs.msdn.microsoft.com/brian_jones/2009/02/19/how-to-copy-a-worksheet-within-a-workbook/

因此,我将using语句的开头更改为:

using (SpreadsheetDocument document = SpreadsheetDocument.Open(Output, true), tmpDocument = SpreadsheetDocument.Create(new MemoryStream(), document.DocumentType))
{
    WorkbookPart wbPart = document.WorkbookPart;
    WorkbookPart tmpWbPart = tmpDocument.AddWorkbookPart();


然后,我不再使用AddNewPart<WorksheetPart>,而是使用以下代码:

// can't directly clone, so add to the temporary workbook part and then back
// into the working workbook part
WorksheetPart tmpWsPart = tmpWbPart.AddPart(inputWsPart);
WorksheetPart replacementWsPart = wbPart.AddPart(tmpWsPart);
tmpWbPart.DeletePart(tmpWsPart);
tmpWsPart = null;


进行了这些更改之后,现在我在工作表中有了我的Drawing部分,在xlsx文件中有相关的文件夹。
因此,打开文件时Excel不再抱怨,并且图形都在那里并已更新。

关于c# - OpenXML writer用法使图形不可读,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38227144/

10-10 18:49