rust zip异步压缩与解压的代码详解
作者:dounine
在使用actix-web框架的时候,如果使用zip解压任务将会占用一个工作线程,因为zip库是同步阻塞的,想用异步非阻塞需要用另一个库,下面介绍下rust zip异步压缩与解压的示例,感兴趣的朋友一起看看吧
在使用actix-web框架的时候,如果使用zip解压任务将会占用一个工作线程,因为zip库是同步阻塞的,想用异步非阻塞需要用另一个库,下面列出同步解压,跟异步解压的两个方法实现,异步解压不会占用工作线程。注意:debug模式下rust异步压缩会很慢,打包成release之后就非常快了。
压缩
依赖
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代码
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}; //读取文件夹文件 async 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(dirs.remove(0)).await?; while let Some(entry) = dir_iter.next_entry().await? { let entry_path_buf = entry.path(); if entry_path_buf.is_dir() { dirs.push(entry_path_buf); } else { files.push(entry_path_buf); } } } Ok(files) } //压缩单个文件 async 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(()); } //压缩 pub 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?; } } writer.close().await?; Ok(()) } //解压 pub 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..zip.file().entries().len() { let entry = zip .file() .entries() .get(index) .ok_or(anyhow!("zip entry not found".to_string()))?; let raw = entry.filename().as_bytes(); let mut file_name = &String::from_utf8_lossy(raw).to_string(); //必需转换为utf8,否则有乱码 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 !p.exists() { 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(()) }
测试
#[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/out.zip")).await?; Ok(()) } #[tokio::test] async fn test_unzip() -> Result<(), anyhow::Error> { let path = Path::new("file/tmp/a/out.zip"); unzip(path, Path::new("file/tmp")).await?; Ok(()) } }
到此这篇关于rust zip异步压缩与解压的文章就介绍到这了,更多相关rust zip解压内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!