Rust边下载边解压tar.gz文件出现UnexpectedEof错误如何解决?
压缩格式处理难度对比
- tar.gz / tar.bz2:二者均为流式压缩+单归档结构,天生支持边下载边处理,无需获取完整文件即可逐块解压提取,使用flate2(处理gz)、bzip2 crate(处理bz2)搭配tar crate即可无缝适配,开发成本最低。
- zip:zip的中央目录存储在文件尾部,要读取归档内的文件列表必须先获取完整文件的末尾数据,几乎无法实现真正的流式边下载边解压,除非提前预知所有文件的偏移量(通用场景下不现实),兼容成本最高。
代码错误根因
你当前的实现存在核心逻辑错误:
- 将逐块返回的下载分片当成了独立的完整压缩包处理,每拿到一个chunk就新建解码器和归档实例,解码器无法积累上下文解析完整的压缩流
Archive::unpack方法要求输入为完整的归档数据流,仅传入单个分片必然会因为数据不完整触发UnexpectedEof报错
修复方案
你需要将整个下载流作为连续输入喂给解码器,而非拆分成分片单独处理:
- 首先在
Cargo.toml中开启对应crate的异步feature:
flate2 = { version = "1.0", features = ["tokio", "gzip"] } tar = { version = "0.4", features = ["tokio"] } tokio-util = { version = "0.7", features = ["io"] }
- 调整代码逻辑,将reqwest的字节流转换为AsyncRead后直接传入解码器:
use tokio_util::io::StreamReader; use flate2::read::AsyncGzDecoder; use tar::Archive; let full_url = format!("{}/{}/{}", HOST, repo_info.url, repo_info.download_url); let response = self.client.get(&full_url).send().await.map_err(|e| Error::new(ErrorKind::InvalidData, e))?; if response.status() == reqwest::StatusCode::OK { // 将字节流转换为AsyncRead实现 let stream = response.bytes_stream(); let read = StreamReader::new( stream.map(|res| res.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))) ); // 传入完整流创建gzip解码器 let gz_decoder = AsyncGzDecoder::new(read); let mut archive = Archive::new(gz_decoder); // 直接解压整个流,内部会自动边接收数据边解压 archive.unpack("./gamedata").await.expect("Cannot unpack archive"); }
如果需要兼容tar.bz2格式,仅需将解码器替换为bzip2::read::AsyncBzDecoder即可,整体逻辑无需调整。
内容的提问来源于stack exchange,提问作者IWantToSpeepZzz
相关产品推荐
相关产品推荐

