我正在研究一个示例,以展示如何读取大文件并分块处理它。然而,它似乎行为不当,返回了无效的读取字节数。
例如,假设我们有一个长度为 8320 字节的测试文件,我们使用以下代码以 4096 字节的块读取它:
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);
var fileLength = new FileInfo(filePath).Length;
using var mm = MemoryMappedFile.CreateFromFile(filePath);
await using var accessor = mm.CreateViewStream();
var bytesRead = 0L;
while (bytesRead < fileLength)
{
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);
}
}
我发现最终迭代仍然返回
read
值 4096。这对我来说似乎是一个错误,但也许我在设置中做错了一些事情。我可以通过显式分割内存缓冲区来解决这个问题,使其成为剩余字节和 4096 之间的最小值,但我确实希望在流中剩余数据较少时调用 ReadAsync 读取 4096 或更少。请注意,我还尝试了另一个带有缓冲区和显式偏移量和计数的 ReadAsync
重载,它具有相同的行为。
此答案全部归功于@MarkGravell:
解决方案很简单,在创建调用中显式设置视图流大小:
var fileLength = new FileInfo(filePath).Length;
using var mm = MemoryMappedFile.CreateFromFile(filePath);
await using var accessor = mm.CreateViewStream(0, fileLength);