用 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<byte></byte>+
MemoryStream做缓冲?仅限 .NET Core 3.0+,且仍需注意 Span 生命周期,别把它逃逸到 async 方法外
真正难的不是切,是切完还能稳稳合回去——每一片的字节边界、文件系统行为、异常中断后的状态恢复,都得在代码里显式兜住。
