Как распаковать данные XZ из гипер :: Response на лету? - PullRequest
0 голосов
/ 20 февраля 2019

Я загружаю файл XZ с hyper , и я хотел бы сохранить его на диск в распакованном виде, извлекая как можно больше из каждого входящего Chunk изапись результатов на диск немедленно, в отличие от первой загрузки всего файла, а затем распаковки.

Существует ящик xz2 , который реализует формат XZ.Однако его XzDecoder, по-видимому, не поддерживает Python-подобную модель decompressobj, где вызывающий абонент неоднократно передает частичный ввод и получает частичный вывод.

Вместо этого XzDecoder получает входные байты через параметр Read, и я не уверен, как склеить эти две вещи вместе.Есть ли способ прокормить Response до XzDecoder?

Единственная подсказка, которую я нашел до сих пор, это проблема , которая содержит ссылку назакрытый тип ReadableChunks, который я теоретически мог бы воспроизвести в своем коде - но, может быть, есть более простой способ?

Ответы [ 2 ]

0 голосов
/ 24 февраля 2019

Основываясь на ответе @ Лейни , я получил следующий рабочий код:

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(())
}
0 голосов
/ 21 февраля 2019

XzDecoder, по-видимому, не поддерживает Python-подобную модель декомпрессии, где вызывающая сторона многократно передает частичный ввод и получает частичный вывод

есть 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();
}
...