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

如何序列化Excel文件流?ClosedXML与Newtonsoft.Json使用遇报错

解决MemoryStream序列化时ReadTimeout报错的问题

问题原因

MemoryStream的ReadTimeout和WriteTimeout属性仅针对异步流场景设计,同步访问时会抛出异常。Newtonsoft.Json默认会序列化对象的所有公共属性,包括这两个不支持同步访问的属性,因此触发报错。

解决方案

方案1:自定义JsonConverter处理MemoryStream

创建自定义转换器,将MemoryStream序列化为字节数组,反序列化时再转回MemoryStream,彻底避开对报错属性的访问:

public class MemoryStreamConverter : JsonConverter<MemoryStream>
{
    public override MemoryStream ReadJson(JsonReader reader, Type objectType, MemoryStream existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var bytes = serializer.Deserialize<byte[]>(reader);
        return bytes == null ? new MemoryStream() : new MemoryStream(bytes);
    }

    public override void WriteJson(JsonWriter writer, MemoryStream value, JsonSerializer serializer)
    {
        var bytes = value.ToArray();
        serializer.Serialize(writer, bytes);
    }
}

使用时在序列化配置中添加该转换器:

var settings = new JsonSerializerSettings();
settings.Converters.Add(new MemoryStreamConverter());

var json = JsonConvert.SerializeObject(yourResultObject, settings);

方案2:将MemoryStream转为字节数组存储

修改Result对象,把MemoryStream类型的字段/属性替换为byte[],需要使用流时再从字节数组重建:

public class Result
{
    // 替换原MemoryStream属性为字节数组
    public byte[] ExcelData { get; set; }

    // 可选:提供快速获取流的方法
    public MemoryStream GetExcelStream()
    {
        return ExcelData == null ? new MemoryStream() : new MemoryStream(ExcelData);
    }
}

生成流时直接转换为字节数组:

using (var workbook = new XLWorkbook(@"C:\Temp\yourfile.xlsx"))
using (var stream = new MemoryStream())
{
    workbook.SaveAs(stream);
    var result = new Result
    {
        ExcelData = stream.ToArray()
    };
    // 序列化Result对象
    var json = JsonConvert.SerializeObject(result);
}

方案3:忽略报错的属性

通过特性或自定义契约解析器,让Newtonsoft.Json跳过ReadTimeout和WriteTimeout属性的序列化:

方法A:使用JsonIgnore特性

如果Result对象直接包含MemoryStream属性,标记忽略:

public class Result
{
    [JsonIgnore]
    public MemoryStream ExcelStream { get; set; }
}

方法B:自定义ContractResolver排除属性

public class StreamContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property.DeclaringType == typeof(MemoryStream) && 
            (property.PropertyName == nameof(MemoryStream.ReadTimeout) || 
             property.PropertyName == nameof(MemoryStream.WriteTimeout)))
        {
            property.Ignored = true;
        }
        return property;
    }
}

使用时配置序列化设置:

var settings = new JsonSerializerSettings
{
    ContractResolver = new StreamContractResolver()
};
var json = JsonConvert.SerializeObject(yourResultObject, settings);

推荐方案

优先选择方案2,字节数组比流更适合序列化存储,避免了流的状态管理问题;如果必须保留MemoryStream类型,则选择方案1的自定义转换器,兼容性更稳定。

内容的提问来源于stack exchange,提问作者Hugo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 15:52:24