OpenXML调用Workbook.Save方法后MemoryStream被释放问题求助
问题解决方案
报错根本原因
ObjectDisposedException由以下几个错误共同触发:
- 原
AddCopyOfSheet方法直接将源文档的WorksheetPart添加到目标文档,该部件的生命周期完全绑定到源SpreadsheetDocument实例,源文档的内部资源被释放后,目标文档访问该关联部件就会抛出对象已释放异常 - 原代码仅添加了工作表部件,未在目标工作簿的
Sheets集合中新增对应的Sheet关联元素,就算没有释放异常,生成的Excel文件也会损坏 - 原代码打开的两个
SpreadsheetDocument实例未主动管理生命周期,GC可能提前回收内部资源触发异常 - 原方案仅保存
Workbook对象,未同步保存样式表、主题、共享字符串表等关联部件,所以会导致高级格式丢失损坏
修正后代码
工作表复制逻辑修正
public static void AddCopyOfSheet(WorkbookPart sourceWorkbookPart, WorkbookPart destWorkbookPart, string sourceSheetName) { // 获取源工作表对应的配置和部件 Sheet sourceSheet = sourceWorkbookPart.Workbook.Descendants<Sheet>().First(s => s.Name.Value.Contains(sourceSheetName)); WorksheetPart sourceWsPart = (WorksheetPart)sourceWorkbookPart.GetPartById(sourceSheet.Id); // 克隆源工作表部件到目标文档,切断和源文档的生命周期绑定 WorksheetPart newWsPart = destWorkbookPart.AddPart<WorksheetPart>(sourceWsPart); // 生成不重复的新SheetId uint maxSheetId = destWorkbookPart.Workbook.Descendants<Sheet>().Max(s => s.SheetId.Value); uint newSheetId = maxSheetId + 1; // 新增Sheet关联元素到目标工作簿 Sheet newSheet = new Sheet() { Id = destWorkbookPart.GetIdOfPart(newWsPart), SheetId = newSheetId, Name = sourceSheetName }; destWorkbookPart.Workbook.Sheets.Append(newSheet); }
主逻辑修正(完全移除本地临时文件写入,性能更高)
public Stream ProcessDocument() { var resultStream = new MemoryStream(); // 加载源Excel文件到内存流 byte[] sourceFileBytes = File.ReadAllBytes("你的源Excel文件路径"); using (var sourceMs = new MemoryStream(sourceFileBytes)) using (var copiedDoc = SpreadsheetDocument.Open(sourceMs, true)) { // 加载带待复制工作表的Excel文件 byte[] sheetSourceBytes = File.ReadAllBytes("你要复制工作表的来源Excel路径"); using (var sheetSourceMs = new MemoryStream(sheetSourceBytes)) using (var sheetSourceDoc = SpreadsheetDocument.Open(sheetSourceMs, false)) { // 复制工作表 AddCopyOfSheet(sheetSourceDoc.WorkbookPart, copiedDoc.WorkbookPart, "foo"); // 保存工作簿修改 copiedDoc.WorkbookPart.Workbook.Save(); } // 完整克隆整个文档到结果流,保留所有格式、部件 copiedDoc.Clone(resultStream); } // 重置流位置,避免调用方读取为空 resultStream.Position = 0; return resultStream; }
说明
- 无需升级OpenXML 2.5版本,上述逻辑可直接运行
- 用
Clone方法完整复制整个文档所有关联部件,不会丢失高级格式、公式、样式等内容 - 所有
SpreadsheetDocument实例都用using包裹,自动管理生命周期,不会出现资源提前释放问题 - 完全移除本地临时文件写入操作,比写入本地副本的方案性能更高
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

