You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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)操作:

  1. Crystal Report导出的Stream通常仅支持单次读写,即使重置位置,原有数据残留会干扰ClosedXML的写入逻辑
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 20:04:57