C#控制台应用ZIP分块合并报‘Unexpected end of archive’错误
ZIP分块拼接报错“Unexpected end of archive”修复方案
核心根因
- ZIP流未完成完整写入就启动拆分:
ZipArchive处于Create模式时,仅在实例执行Dispose(即离开using(zipArchive)代码块)后,才会写入ZIP格式强制要求的中央目录、文件结束标记(EOCD)数据。原代码在zipArchive仍存活、未完成最终写入的阶段就重置流位置读取数据,拆分得到的分块本身就是残缺的ZIP片段,拼接后必然缺失ZIP结构的尾部校验标记。这也是普通文本文件拆分拼接正常、但ZIP会报错的核心原因——普通文本写入后即为完整结构,而ZIP的结构需要最终写入阶段的收尾数据才能合法。 - 分块缓存无差别写入冗余数据:拆分时直接将固定100KB大小的
buffer加入分块列表,未截取本次Read实际读取的字节长度。如果最后一块ZIP数据不足100KB,buffer中剩余的未填充字节会被一并加入分块,进一步破坏文件完整性。
修复后代码
修正后的CreateChunks分块方法
public List<byte[]> CreateChunks(string fileName, string txtFilePath) { const int chunkSize = 100 * 1000; // 100KB var fileChunks = new List<byte[]>(); using (var memoryStream = new MemoryStream()) { // leaveOpen设为true:ZipArchive Dispose时不会连带关闭内部的memoryStream using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) { zipArchive.CreateEntryFromFile(txtFilePath, fileName, CompressionLevel.Optimal); } // 离开该代码块时ZipArchive完成所有ZIP结构的写入 // 待ZIP完全写入流后,再开始拆分逻辑 memoryStream.Position = 0; var buffer = new byte[chunkSize]; int readBytes; // 仅截取实际读取的有效字节生成分块 while ((readBytes = memoryStream.Read(buffer, 0, buffer.Length)) > 0) { var validChunk = new byte[readBytes]; Array.Copy(buffer, 0, validChunk, 0, readBytes); fileChunks.Add(validChunk); } } return fileChunks; }
健壮性优化后的JoinChunks拼接方法
原方法逻辑可正常工作,优化后可避免资源泄漏、路径拼接异常问题:
public void JoinChunks(List<byte[]> fileChunks, string outputDirectory) { // 使用Path.Combine拼接路径,避免硬编码斜杠导致的跨平台路径异常 string outputZipPath = Path.Combine(outputDirectory, "temp.zip"); using (var memoryStream = new MemoryStream()) using (var fileStream = new FileStream(outputZipPath, FileMode.Create)) { foreach (var chunk in fileChunks) { memoryStream.Write(chunk, 0, chunk.Length); } memoryStream.WriteTo(fileStream); } }
修复验证点
- 拆分逻辑执行时机调整到ZipArchive完全Dispose之后,确保拆分的是包含完整EOCD标记的合法ZIP流
- 每个分块仅包含实际读取的有效字节,无冗余数据
- 拼接后得到的ZIP文件结构完整,可正常解压,不会触发“Unexpected end of archive”报错
内容的提问来源于stack exchange,提问作者Dinesh
相关产品推荐
相关产品推荐

