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

MemoryMappedViewStream.ReadAsync读取超出内存映射文件末尾问题

MemoryMappedViewStream读取最后一块时返回满chunkSize的问题分析与解决

你遇到的问题是MemoryMappedViewStream的ReadAsync行为特性导致的,而非你的代码逻辑错误。当使用内存映射文件的视图流读取时,即使文件剩余字节不足chunkSize,ReadAsync仍会返回请求的全部长度(剩余部分补0),而非实际剩余的有效字节数。

以你的测试场景为例:文件总长度8320字节,chunkSize=4096,前两次读取共8192字节,剩余128字节,但MemoryMappedViewStream的ReadAsync会读取4096字节(其中128是有效数据,剩下3968字节为0),因此read值始终是4096。

解决方案

方案一:显式计算剩余字节数,限制每次读取长度

在每次读取前计算剩余未处理的字节数,只读取对应长度的数据,避免读取到填充的0字节:

public static async IAsyncEnumerable<byte[]> ConvertInChunks(string filePath, int chunkSize,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    byte[]? buffer = null;
    try
    {
        buffer = ArrayPool<byte>.Shared.Rent(chunkSize);
        var fileLength = new FileInfo(filePath).Length;
        using var mm = MemoryMappedFile.CreateFromFile(filePath);
        await using var accessor = mm.CreateViewStream();

        var bytesRead = 0L;
        while (bytesRead < fileLength)
        {
            // 计算剩余字节数,取chunkSize和剩余字节的较小值作为本次读取长度
            var remainingBytes = fileLength - bytesRead;
            var readLength = (int)Math.Min(chunkSize, remainingBytes);
            var memory = buffer.AsMemory(0, readLength);
            
            var read = await accessor.ReadAsync(memory, cancellationToken);
            bytesRead += read;

            yield return memory[..read].ToArray();
        }
    }
    finally
    {
        if (buffer is not null)
            ArrayPool<byte>.Shared.Return(buffer);
    }
}

方案二:改用FileStream替代内存映射文件

如果你的场景不需要内存映射文件的特性,直接使用FileStream更符合预期,它的ReadAsync会在文件末尾返回实际剩余的字节数:

public static async IAsyncEnumerable<byte[]> ConvertInChunks(string filePath, int chunkSize,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    byte[]? buffer = null;
    try
    {
        buffer = ArrayPool<byte>.Shared.Rent(chunkSize);
        var memory = buffer.AsMemory(0, chunkSize);

        await using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: chunkSize, useAsync: true);
        
        int read;
        while ((read = await stream.ReadAsync(memory, cancellationToken)) > 0)
        {
            yield return memory[..read].ToArray();
        }
    }
    finally
    {
        if (buffer is not null)
            ArrayPool<byte>.Shared.Return(buffer);
    }
}

原因说明

内存映射文件是将文件内容映射到进程的虚拟内存空间,读取操作基于内存页机制实现。当读取到文件末尾时,系统会自动填充0来填满当前内存页,因此ViewStream的ReadAsync会返回请求的全部长度,而非实际剩余的有效数据量。这是内存映射文件的设计特性,并非bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 11:01:25