FileStream分块读写是最稳妥方案,需控制缓冲区大小、显式指定文件模式、用序号命名分片、写.meta校验文件、合并时严格校验每片长度与完整性。

用 FileStream 分块读写是最稳妥的方案
大文件不能一次性加载进内存,否则会触发 OutOfMemoryException。必须用流式处理,逐段读取、逐段写入。核心是控制缓冲区大小(如 8192 字节),避免小块太碎影响 I/O 效率,也避免太大吃光内存。
关键点:
-
FileStream要显式指定FileMode.Open和FileAccess.Read,源文件不能被其他进程独占锁定 - 每个分片文件用
FileMode.Create,确保覆盖旧碎片 - 用
stream.Read(buffer, 0, buffer.Length)返回值判断是否读到末尾——它可能小于 buffer 长度,不能假设每次读满 - 别用
File.ReadAllBytes()或StreamReader.ReadToEnd(),它们会把整个文件塞进内存
分片命名和索引要可预测、可还原
切割后的文件必须能无歧义地合并回原文件,所以命名不能依赖随机数或时间戳(并发或重跑时会冲突)。推荐用序号 + 原文件名前缀 + 固定后缀,比如 archive.zip.001、archive.zip.002。
实操建议:
- 用
string.Format("{0}.{1:D3}", baseName, index)保证序号三位对齐,排序时不会出现.1、.10、.2这种错序 - 在首片里额外写一个
.meta文件(如archive.zip.meta),记录总片数、原始大小、校验和(SHA256),方便校验完整性 - 不要把分片扩展名改成
.part或.seg等自定义后缀——某些系统或工具会忽略它们,直接用.001更通用
合并时必须按序读取且严格校验长度
合并不是简单把所有分片 File.AppendAllBytes() 拼起来。如果某片损坏或缺失,后面所有数据都会偏移,最终文件大概率无法打开。
安全做法:
- 先按命名顺序枚举所有分片文件,用
Directory.GetFiles(dir, $"{baseName}.*")+ 正则提取序号并排序 - 每个分片打开为
FileStream,用stream.Length校验是否等于预期大小(最后一片除外) - 写入目标文件时,用
outputStream.Write(buffer, 0, bytesRead),其中bytesRead来自上一步Read()的返回值,不是 buffer.Length - 合并完成后,计算整个输出文件的
SHA256并与.meta中记录的比对
注意不同场景下的边界处理
真实环境里文件来源复杂:网络流、加密容器、只读介质、UWP 沙盒……这些会影响 FileStream 的构造方式和权限。
常见陷阱:
- 从
HttpWebResponse.GetResponseStream()切割?不行——它不支持Seek(),只能顺序读一次,得先落地为临时文件再切 - 目标路径在 OneDrive 或 WSL 挂载点下?检查
Path.GetFullPath()是否含非法字符或过长路径(Windows 限制 MAX_PATH=260) - 需要支持断点续切?得在临时目录存一个
.progress文件,记录已写入字节数和当前分片索引,重启时跳过已写完的片 - 用
Span+MemoryStream做缓冲?仅限 .NET Core 3.0+,且仍需注意 Span 生命周期,别把它逃逸到 async 方法外
真正难的不是切,是切完还能稳稳合回去——每一片的字节边界、文件系统行为、异常中断后的状态恢复,都得在代码里显式兜住。









