使用ClosedXML修改Crystal Report导出流时SaveAs抛出异常求助
问题解决:ClosedXML SaveAs Crystal Report导出Stream时抛出ArgumentOutOfRangeException
问题复现
使用Crystal Report导出XLSX格式的Stream,再用ClosedXML(0.102.3)加载该Stream并尝试保存回同一个Stream时,执行workbook.SaveAs(excelStream)抛出异常:
System.ArgumentOutOfRangeException: 'Not implemented value! Parameter name: value'
相关代码如下:
ReportDocument crReportDocument = new ReportDocument(); crReportDocument.Load("D:\\Sample\\test.rpt"); ConnectionInfo connInfo = new ConnectionInfo { ServerName = "connectionString" }; foreach (ReportDocument subRpt in crReportDocument.Subreports) { foreach (Table crTable in subRpt.Database.Tables) { TableLogOnInfo logInfo = new TableLogOnInfo { ConnectionInfo = connInfo }; crTable.ApplyLogOnInfo(logInfo); } } foreach (Table crTable in crReportDocument.Database.Tables) { TableLogOnInfo logInfo = new TableLogOnInfo { ConnectionInfo = connInfo }; crTable.ApplyLogOnInfo(logInfo); } var exportOptions = new ExportOptions { ExportFormatType = ExportFormatType.XLSXPagebased }; var excelFormatOptions = new ExcelFormatOptions { ExcelUseConstantColumnWidth = false, ShowGridLines = true }; exportOptions.ExportFormatOptions = excelFormatOptions; var exportRequestContext = new ExportRequestContext { ExportInfo = exportOptions }; Stream excelStream = crReportDocument.FormatEngine.ExportToStream(exportRequestContext); if (excelStream.CanSeek) { excelStream.Position = 0; } //ClosedXML read begin using (var workbook = new XLWorkbook(excelStream)) { var worksheet = workbook.Worksheets.First(); var b = worksheet.Cell("A2"); workbook.SaveAs(excelStream); }
核心原因
不能在同一个Stream实例上同时完成读取(加载XLWorkbook)和写入(SaveAs)操作:
- Crystal Report导出的Stream通常仅支持单次读写,即使重置位置,原有数据残留会干扰ClosedXML的写入逻辑
- ClosedXML的SaveAs操作需要干净的可写流,复用读取过的流会导致内部格式解析冲突,触发未实现的参数异常
解决方案
使用独立的MemoryStream中转数据,分离读取和写入操作,修改后的代码如下:
ReportDocument crReportDocument = new ReportDocument(); crReportDocument.Load("D:\\Sample\\test.rpt"); ConnectionInfo connInfo = new ConnectionInfo { ServerName = "connectionString" }; // 处理子报表和主报表的登录信息 foreach (ReportDocument subRpt in crReportDocument.Subreports) { foreach (Table crTable in subRpt.Database.Tables) { TableLogOnInfo logInfo = new TableLogOnInfo { ConnectionInfo = connInfo }; crTable.ApplyLogOnInfo(logInfo); } } foreach (Table crTable in crReportDocument.Database.Tables) { TableLogOnInfo logInfo = new TableLogOnInfo { ConnectionInfo = connInfo }; crTable.ApplyLogOnInfo(logInfo); } // 导出Crystal报表到Stream var exportOptions = new ExportOptions { ExportFormatType = ExportFormatType.XLSXPagebased, ExportFormatOptions = new ExcelFormatOptions { ExcelUseConstantColumnWidth = false, ShowGridLines = true } }; var exportRequestContext = new ExportRequestContext { ExportInfo = exportOptions }; using (var originalExportStream = crReportDocument.FormatEngine.ExportToStream(exportRequestContext)) { // 将原始导出流复制到可读写的MemoryStream using (var tempStream = new MemoryStream()) { originalExportStream.CopyTo(tempStream); tempStream.Position = 0; // 用ClosedXML加载并修改Excel using (var workbook = new XLWorkbook(tempStream)) { var worksheet = workbook.Worksheets.First(); var targetCell = worksheet.Cell("A2"); // 这里添加你的修改逻辑,例如 targetCell.Value = "修改后的值"; // 重置临时流,准备写入修改后的内容 tempStream.Position = 0; tempStream.SetLength(0); // 清空原有数据 workbook.SaveAs(tempStream); // 处理修改后的流,例如保存到文件 tempStream.Position = 0; using (var outputFileStream = new FileStream("D:\\Sample\\modified_test.xlsx", FileMode.Create, FileAccess.Write)) { tempStream.CopyTo(outputFileStream); } // 如果需要返回Stream给调用方,可将tempStream的内容复制到新的MemoryStream返回 // var resultStream = new MemoryStream(); // tempStream.Position = 0; // tempStream.CopyTo(resultStream); // resultStream.Position = 0; // return resultStream; } } }
关键修改点
- 用
MemoryStream中转Crystal导出的内容,确保流支持双向读写 - 在SaveAs前重置
MemoryStream的位置并清空内容,避免旧数据干扰 - 分离读取和写入的流实例,彻底解决冲突问题
内容的提问来源于stack exchange,提问作者darkwindstom
相关产品推荐
相关产品推荐

