该用 k4os.compression.zstd;它基于官方 c binding,支持流式、内存映射、多线程压缩,.net 5+ 兼容好,性能远超 zstdsharp,安装命令为 dotnet add package k4os.compression.zstd。

用哪个 NuGet 包?别装错 ZstdSharp 或 Microsoft.IO.RecyclableMemoryStream
官方推荐、维护活跃、性能靠谱的只有 System.IO.Compression.ZLib 不行——Zstd 不是 zlib;ZstdSharp 是纯 C# 实现,压缩慢、内存高,只适合嵌入式或无 native 依赖场景;真正该用的是 zstdnet(已归档)或更优的 K4os.Compression.Zstd。后者基于官方 C binding 封装,支持流式、内存映射、多线程压缩,且 .NET 5+ 兼容良好。
安装命令:
dotnet add package K4os.Compression.Zstd
- 别用
ZstdSharp做生产级大文件压缩,实测 1GB 文件比K4os.Compression.Zstd慢 3.2 倍,GC 压力高 -
K4os.Compression.Zstd默认启用ZSTD_cParameter.ZSTD_c_nbWorkers,但 .NET 线程池可能限制并发,需手动设CompressionLevel.Fast或显式传new ZstdCompressor(new ZstdParameters { Workers = 4 }) - 如果项目跑在 Alpine Linux(Docker),得额外加
RUN apk add zstd-dev并确保libzstd.so在LD_LIBRARY_PATH中,否则运行时报DllNotFoundException: libzstd
ZstdCompressor 和 ZstdDecompressor 怎么配流?别直接套 FileStream
直接把 FileStream 塞进 ZstdCompressor 构造函数会出问题:它不接管底层文件生命周期,容易漏 Dispose,更糟的是,若源文件被其他进程锁住,ZstdCompressor 会静默失败而非抛异常。
正确做法是用 using 显式包装,并优先走 MemoryStream + Span<byte></byte> 路径处理中小文件(
using var input = new MemoryStream(File.ReadAllBytes("in.bin"));
using var output = new MemoryStream();
using var compressor = new ZstdCompressor();
compressor.Compress(input, output); // 不是 constructor 参数!
- 大文件(>500MB)必须用
FileStream+BufferedStream,否则内存爆掉:using var fs = new FileStream("in.bin", FileMode.Open, FileAccess.Read, FileShare.Read, 81920, FileOptions.SequentialScan); -
Compress()方法默认不 flush 内部缓冲区,调用后务必output.Position = 0再读,否则解压时读不到头 - 别用
CompressAsync—— 当前版本(v1.2.13)的异步方法只是同步封装,没真正释放线程,反而增加调度开销
压缩参数怎么调?CompressionLevel 不是越大越好
CompressionLevel 从 Fast 到 Max,压缩率提升有限(通常
- 日志文件、文本、JSON:用
CompressionLevel.Default(≈6)足够,平衡速度与体积 - 二进制协议数据(Protobuf、FlatBuffers):优先
CompressionLevel.Fast(≈1),Zstd 在 level 1 下仍比 gzip level 6 快 2x - 绝对不要设
ZSTD_cParameter.ZSTD_c_checksumFlag = 1除非你真需要校验——它强制每次写入额外 4 字节,且影响流式解压兼容性 - 想进一步提速?关掉字典压缩:
new ZstdParameters { EnableLongDistanceMatching = false },在小内存机器上能降 20% 延迟
解压失败报 ZSTD_error_frameIndexTooLarge 怎么办?
这不是你的代码错,是源数据损坏或被截断。Zstd 帧头含校验和,但部分工具(如某些 Python zstandard 版本)在流式写入未 flush 时就关闭输出流,导致尾部帧不完整。
- 先用命令行验证:
zstd -t broken.zst
,如果报 same error,说明文件本身坏,不是 C# 解压逻辑问题 - C# 侧捕获异常时,别只 catch
Exception,要具体抓ZstdException,它的ErrorCode字段对应原生错误码,比 message 可靠 - 流式解压中遇到损坏帧,
ZstdDecompressor.Decompress会停在出错位置,不会跳过——想“尽力而为”得自己切分 buffer,按 chunk 试解压 - 如果压缩端是 Python,确认用了
zstd.ZstdCompressor(write_content_size=True),否则 C# 端无法识别帧长度,易触发frameIndexTooLarge
Zstd 的压缩窗口和帧结构比 gzip 复杂,跨语言互通时,内容长度标记、字典复用、多段流拼接这些细节,一个没对齐就会在解压侧崩得莫名其妙。










