如何将Span参数的Stream.Read改写为byte数组重载适配.NET Framework 4.6.1
问题背景
计划在.NET Framework 4.6.1项目中运行libvlcsharp的预览缩略图提取示例,原示例基于.NET 6编写,存在API不兼容导致的编译错误。
原示例核心处理代码如下:
private static async Task ProcessThumbnailsAsync(string destination, CancellationToken token) { var frameNumber = 0; while (!token.IsCancellationRequested) { if (FilesToProcess.TryDequeue(out var file)) { using (var image = new Image<SixLabors.ImageSharp.PixelFormats.Bgra32>((int)(Pitch / BytePerPixel), (int)Lines)) using (var sourceStream = file.file.CreateViewStream()) { var mg = image.GetPixelMemoryGroup(); for(int i = 0; i < mg.Count; i++) { sourceStream.Read(MemoryMarshal.AsBytes(mg[i].Span)); } Console.WriteLine($"Writing {frameNumber:0000}.jpg"); var fileName = Path.Combine(destination, $"{frameNumber:0000}.jpg"); using (var outputFile = File.Open(fileName, FileMode.Create)) { image.Mutate(ctx => ctx.Crop((int)Width, (int)Height)); image.SaveAsJpeg(outputFile); } } file.accessor.Dispose(); file.file.Dispose(); frameNumber++; } else { await Task.Delay(TimeSpan.FromSeconds(1), token); } } }
编译失败的具体代码行:
sourceStream.Read(MemoryMarshal.AsBytes(mg[i].Span));
报错原因
两个框架版本的Stream读取API存在差异:
- .NET 6 提供
Read(Span<byte>)重载,支持直接将流内容读取到Span指向的非托管/托管连续内存块 - .NET Framework 4.x 仅提供
Read(Byte[], Int32, Int32)重载,仅支持将流内容读取到字节数组中,无法直接传入Span参数
兼容修改方案
通过临时字节数组中转读取结果,再拷贝回原内存位置,完全保留原有逻辑,修改后的内存块读取循环代码如下:
var mg = image.GetPixelMemoryGroup(); for(int i = 0; i < mg.Count; i++) { Span<byte> targetSpan = MemoryMarshal.AsBytes(mg[i].Span); // 申请和目标内存块长度匹配的临时字节数组 byte[] tempBuffer = new byte[targetSpan.Length]; // 调用.NET Framework 4.x支持的Read方法读取流数据 int readCount = sourceStream.Read(tempBuffer, 0, tempBuffer.Length); // 将读取到的内容拷贝回原Span对应的内存位置 tempBuffer.AsSpan(0, readCount).CopyTo(targetSpan); }
补充说明
- 该修改依赖
System.Memory包,SixLabors.ImageSharp会自动引入该兼容包,无需额外手动安装 - 修改后逻辑和原代码完全一致:按像素内存组的分块顺序逐块读取流数据,不会出现像素错位、内存越界问题
- 临时字节数组随单次循环生命周期自动回收,不会造成内存泄漏
内容的提问来源于stack exchange,提问作者TripleAntigen
相关产品推荐
相关产品推荐

