Optimize extract_zip: Use async_zip::read::stream::ZipFileReader to avoid temporary file (#590)

* Add new dep async_zip v0.0.9 to binstalk-downloader
   with features "gzip", "zstd", "xz", "bzip2", "tokio".
* Refactor: Simplify `async_extracter::extract_*` API
* Refactor: Create newtype wrapper of `ZipError`
   so that the zip can be upgraded without affecting API of this crate.
* Enable feature fs of dep tokio in binstalk-downloader
* Rewrite `extract_zip` to use `async_zip::read::stream::ZipFileReader`
   which avoids writing the zip file to a temporary file and then read it
   back into memory.
* Refactor: Impl new fn `await_on_option` and use it
* Optimize `tokio::select!`: Make them biased and check for cancellation first
  to make cancellation takes effect ASAP.
* Rm unused dep zip from binstalk-downloader

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2022-12-12 11:44:34 +11:00 committed by GitHub
parent e6f969245a
commit 3b1a7f2c78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 372 additions and 94 deletions

View file

@ -7,7 +7,6 @@ use thiserror::Error as ThisError;
use tracing::{debug, instrument};
pub use binstalk_types::cargo_toml_binstall::PkgFmt;
pub use zip::result::ZipError;
use crate::remote::{Client, Error as RemoteError, Url};
@ -20,6 +19,12 @@ pub use async_tar_visitor::*;
mod extracter;
mod stream_readable;
mod zip_extraction;
pub use zip_extraction::ZipError;
mod utils;
use utils::await_on_option;
pub type CancellationFuture = Option<Pin<Box<dyn Future<Output = Result<(), io::Error>> + Send>>>;
#[derive(Debug, ThisError)]
@ -112,15 +117,13 @@ impl Download {
debug!("Downloading and extracting then in-memory processing");
let ret = if let Some(cancellation_future) = cancellation_future {
tokio::select! {
res = extract_tar_based_stream_and_visit(stream, fmt, visitor) => res?,
res = cancellation_future => {
Err(res.err().unwrap_or_else(|| io::Error::from(DownloadError::UserAbort)))?
}
let ret = tokio::select! {
biased;
res = await_on_option(cancellation_future) => {
Err(res.err().unwrap_or_else(|| io::Error::from(DownloadError::UserAbort)))?
}
} else {
extract_tar_based_stream_and_visit(stream, fmt, visitor).await?
res = extract_tar_based_stream_and_visit(stream, fmt, visitor) => res?,
};
debug!("Download, extraction and in-memory procession OK");
@ -145,7 +148,11 @@ impl Download {
path: &Path,
cancellation_future: CancellationFuture,
) -> Result<(), DownloadError> {
let stream = this.client.get_stream(this.url).await?;
let stream = this
.client
.get_stream(this.url)
.await?
.map(|res| res.map_err(DownloadError::from));
debug!("Downloading and extracting to: '{}'", path.display());