Rust中如何使用flate2获取gzip未压缩大小(无需读取全文件)
gzip未压缩大小获取方案
你设想的header.filesz()类方法无法直接使用,原因是gzip规范中未压缩大小字段(ISIZE)存储在gzip文件的尾部最后4字节,而非文件头部,刚初始化解码器时还未读取到该位置,无法直接获取。
根据你的精度、性能需求,可以选择以下两种实现方案,两种方案都不需要把完整解压后的文件加载到内存:
方案1:直接读取尾部字段(速度最快,有限制)
单段非拼接gzip文件的尾部4字节以小端序存储了未压缩大小对2^32取模的结果,你只需要跳转到文件对应位置读取即可,完全不需要解压操作,性能极高。
use std::fs::File; use std::io::{self, Read, Seek, SeekFrom}; /// 仅适用于单段gzip、未压缩大小小于4GiB的场景 fn get_gzip_uncompressed_size_fast(file: &mut File) -> io::Result<u64> { file.seek(SeekFrom::End(-4))?; let mut size_buf = [0u8; 4]; file.read_exact(&mut size_buf)?; Ok(u32::from_le_bytes(size_buf) as u64) } fn main() -> io::Result<()> { let mut file = File::open("file1.gz")?; let uncompressed_size = get_gzip_uncompressed_size_fast(&mut file)?; println!("未压缩大小:{} 字节", uncompressed_size); Ok(()) }
该方案限制:
- 无法处理多段拼接的gzip文件,读到的只是最后一段的大小
- 未压缩大小超过4GiB时,返回值是实际大小对4GiB取模的结果,数值不准确
方案2:流式解压读取总输出大小(结果100%准确)
不管是单段gzip还是多段拼接gzip,不管文件多大,你都可以用解码器流式读取内容,把解压输出直接丢弃到空输出流(不占用内存存储完整解压内容),读完整个流后通过total_out()方法拿到准确的总未压缩大小。
单gzip流实现:
use std::fs::File; use std::io::{self, Read}; use flate2::read::GzDecoder; fn get_gzip_size_accurate(path: &str) -> io::Result<u64> { let file = File::open(path)?; let mut decoder = GzDecoder::new(file); // 流式解压,输出直接丢弃,不占内存 io::copy(&mut decoder, &mut io::sink())?; Ok(decoder.total_out()) }
多段拼接gzip(MultiGzDecoder)实现:
use std::fs::File; use std::io::{self, Read}; use flate2::read::MultiGzDecoder; fn get_multi_gzip_size_accurate(path: &str) -> io::Result<u64> { let file = File::open(path)?; let mut decoder = MultiGzDecoder::new(file); io::copy(&mut decoder, &mut io::sink())?; Ok(decoder.total_out()) }
该方案说明:
- 不需要加载完整解压内容到内存,内存占用恒定
- 会完整执行一遍解压校验逻辑,速度比直接读尾部字段慢,但结果完全准确,无4GiB大小限制、兼容多段拼接gzip
注意:不存在直接从gzip头部读取未压缩大小的方法,gzip头部不存储该字段,所有号称不读文件就能拿到精确大小的方法都不符合gzip规范。
内容的提问来源于stack exchange,提问作者JamesThomasMoon
相关产品推荐
相关产品推荐

