当我编写相当大的xlsx文件时,我按照此页面上的建议使用OpenXmlReader
和OpenXmlWriter
:
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文件夹中缺少两个文件夹:
charts
和drawings
显然,我缺少将这些代码添加到最终存档中的代码,但是我(尚未)弄清楚这是什么代码。
最佳答案
当总是要克隆输入部分时,以上代码在循环内创建了一个新的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/