如何即时从 hyper::Response 解压缩 XZ 数据?
Posted
技术标签:
【中文标题】如何即时从 hyper::Response 解压缩 XZ 数据?【英文标题】:How to decompress XZ data from a hyper::Response on the fly? 【发布时间】:2019-02-20 15:23:47 【问题描述】:我正在下载一个带有hyper 的XZ 文件,我想通过从每个传入的Chunk
中提取尽可能多的内容并将结果立即写入磁盘,而不是首先将其以解压缩形式保存到磁盘下载整个文件然后解压。
有实现 XZ 格式的 xz2 crate。但是,它的XzDecoder
似乎不支持Python-like decompressobj
模型,在该模型中,调用者反复提供部分输入并获得部分输出。
相反,XzDecoder
通过Read
参数接收输入字节,我不知道如何将这两个东西粘合在一起。有没有办法将Response
提供给XzDecoder
?
到目前为止,我发现的唯一线索是 issue,它包含对私有 ReadableChunks
类型的引用,理论上我可以在我的代码中复制它 - 但也许有更简单的方法?
【问题讨论】:
【参考方案1】:
XzDecoder
似乎不支持类似 Python 的 decompressobj 模型,其中调用者重复提供部分输入并获得部分输出
xz2::stream::Stream
正是您想要的。非常粗糙的未经测试的代码,需要适当的错误处理等,但我希望你能明白:
fn process(body: hyper::body::Body)
let mut decoder = xz2::stream::Stream::new_stream_decoder(1000, 0).unwrap();
body.for_each(|chunk|
let mut buf: Vec<u8> = Vec::new();
if let Ok(_) = decoder.process_vec(&chunk, &mut buf, Action::Run)
// write buf to disk
Ok(())
).wait().unwrap();
【讨论】:
谢谢,这成功了! Here 是我最终得到的。 @mephi42 请将该代码作为单独的答案发布。评论非常短暂。【参考方案2】:基于@Laney's answer,我想出了以下工作代码:
extern crate failure;
extern crate hyper;
extern crate tokio;
extern crate xz2;
use std::fs::File;
use std::io::Write;
use std::u64;
use failure::Error;
use futures::future::done;
use futures::stream::Stream;
use hyper::Body, Chunk, Response;
use hyper::rt::Future;
use hyper_tls::HttpsConnector;
use tokio::runtime::Runtime;
fn decode_chunk(file: &mut File, xz: &mut xz2::stream::Stream, chunk: &Chunk)
-> Result<(), Error>
let end = xz.total_in() as usize + chunk.len();
let mut buf = Vec::with_capacity(8192);
while (xz.total_in() as usize) < end
buf.clear();
xz.process_vec(
&chunk[chunk.len() - (end - xz.total_in() as usize)..],
&mut buf,
xz2::stream::Action::Run)?;
file.write_all(&buf)?;
Ok(())
fn decode_response(mut file: File, response: Response<Body>)
-> impl Future<Item=(), Error=Error>
done(xz2::stream::Stream::new_stream_decoder(u64::MAX, 0)
.map_err(Error::from))
.and_then(|mut xz| response
.into_body()
.map_err(Error::from)
.for_each(move |chunk| done(
decode_chunk(&mut file, &mut xz, &chunk))))
fn main() -> Result<(), Error>
let client = hyper::Client::builder().build::<_, hyper::Body>(
HttpsConnector::new(1)?);
let file = File::create("hello-2.7.tar")?;
let mut runtime = Runtime::new()?;
runtime.block_on(client
.get("https://ftp.gnu.org/gnu/hello/hello-2.7.tar.xz".parse()?)
.map_err(Error::from)
.and_then(|response| decode_response(file, response)))?;
runtime.shutdown_now();
Ok(())
【讨论】:
以上是关于如何即时从 hyper::Response 解压缩 XZ 数据?的主要内容,如果未能解决你的问题,请参考以下文章