When using the actix-web framework, if you use zip decompression task, it will occupy one worker thread, because the zip library is synchronously blocked, and you need to use another library to use asynchronous non-blocking. The following are two methods of synchronous decompression and asynchronous decompression. Asynchronous decompression will not occupy the worker thread. Note: rust asynchronous compression in debug mode will be very slow, and it will be very fast after packaging into release.
compression
rely
tokio = { version = "1.35.1", features = ["macros"] } tokio-util = "0.7.10" async_zip = { version = "0.0.17", features = ["tokio", "tokio-fs", "deflate"] } futures-lite = "2.3.0" anyhow = "1.0.44"
rust code
use anyhow::anyhow; use async_zip::tokio::read::seek::ZipFileReader; use async_zip::tokio::write::ZipFileWriter; use async_zip::{Compression, DeflateOption, ZipEntryBuilder}; use futures_lite::AsyncWriteExt; use std::path::{Path, PathBuf}; use tokio::fs::File; use tokio::fs::{create_dir_all, OpenOptions}; use tokio::fs::{read_dir, remove_dir_all}; use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader}; use tokio_util::compat::TokioAsyncWriteCompatExt; use tokio_util::compat::{FuturesAsyncWriteCompatExt, TokioAsyncReadCompatExt}; //Read folder fileasync fn dirs(dir: PathBuf) -> Result<Vec<PathBuf>, anyhow::Error> { let mut dirs = vec![dir]; let mut files = vec![]; while !dirs.is_empty() { let mut dir_iter = read_dir((0)).await?; while let Some(entry) = dir_iter.next_entry().await? { let entry_path_buf = (); if entry_path_buf.is_dir() { (entry_path_buf); } else { (entry_path_buf); } } } Ok(files) } //Compress a single fileasync fn zip_entry( input_path: &Path, file_name: &str, zip_writer: &mut ZipFileWriter<File>, ) -> Result<(), anyhow::Error> { let mut input_file = File::open(input_path).await?; let builder = ZipEntryBuilder::new(file_name.into(), Compression::Deflate) .deflate_option(DeflateOption::Normal); let mut entry_writer = zip_writer.write_entry_stream(builder).await?; futures_lite::io::copy(&mut input_file.compat(), &mut entry_writer).await?; entry_writer.close().await?; return Ok(()); } //compressionpub async fn zip(input_path: &Path, out_path: &Path) -> Result<(), anyhow::Error> { let file = File::create(out_path).await?; let mut writer = ZipFileWriter::with_tokio(file); let input_dir_str = input_path .as_os_str() .to_str() .ok_or(anyhow!("Input path not valid UTF-8."))?; if input_path.is_file() { let file_name = input_path .file_name() .ok_or(anyhow!("File name not found.".to_string()))? .to_string_lossy(); zip_entry(input_path, &file_name, &mut writer).await?; } else { let entries = dirs(input_path.into()).await?; for entry_path_buf in entries { let entry_path = entry_path_buf.as_path(); let entry_str = entry_path .as_os_str() .to_str() .ok_or(anyhow!("Directory file path not valid UTF-8."))?; let file_name = &entry_str[(input_dir_str.len() + 1)..]; zip_entry(entry_path, file_name, &mut writer).await?; } } ().await?; Ok(()) } //Decompressionpub async fn unzip<T: AsRef<Path>>(path: T, out_path: T) -> Result<(), anyhow::Error> { let out_path = out_path.as_ref(); if out_path.exists() { remove_dir_all(out_path).await?; } else { create_dir_all(out_path).await?; } let path = path.as_ref(); let file = File::open(path).await?; let reader = BufReader::new(file); let mut zip = ZipFileReader::with_tokio(reader).await?; for index in 0..().entries().len() { let entry = zip .file() .entries() .get(index) .ok_or(anyhow!("zip entry not found".to_string()))?; let raw = ().as_bytes(); let mut file_name = &String::from_utf8_lossy(raw).to_string(); //It must be converted to utf8, otherwise there will be garbled code let zip_path = out_path.join(file_name); if file_name.ends_with("/") { create_dir_all(&zip_path).await?; continue; } if let Some(p) = zip_path.parent() { if !() { create_dir_all(p).await?; } } let mut entry_reader = zip.reader_without_entry(index).await?; let mut writer = OpenOptions::new() .write(true) .create_new(true) .open(&zip_path) .await?; futures_lite::io::copy(&mut entry_reader, &mut writer.compat_write()).await?; } Ok(()) }
test
#[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_zip() -> Result<(), anyhow::Error> { let path = Path::new("file/tmp/test"); zip(path, Path::new("file/tmp/")).await?; Ok(()) } #[tokio::test] async fn test_unzip() -> Result<(), anyhow::Error> { let path = Path::new("file/tmp/a/"); unzip(path, Path::new("file/tmp")).await?; Ok(()) } }
This is the end of this article about asynchronous compression and decompression of rust zip. For more related content on rust zip decompression, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!