rust?zip異步壓縮與解壓的代碼詳解
在使用actix-web框架的時候,如果使用zip解壓任務(wù)將會占用一個工作線程,因為zip庫是同步阻塞的,想用異步非阻塞需要用另一個庫,下面列出同步解壓,跟異步解壓的兩個方法實現(xiàn),異步解壓不會占用工作線程。注意: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(); //必需轉(zhuǎn)換為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(())
}
}到此這篇關(guān)于rust zip異步壓縮與解壓的文章就介紹到這了,更多相關(guān)rust zip解壓內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust?use關(guān)鍵字妙用及模塊內(nèi)容拆分方法
這篇文章主要介紹了Rust?use關(guān)鍵字妙用|模塊內(nèi)容拆分,文中還給大家介紹use關(guān)鍵字的習(xí)慣用法,快速引用自定義模塊內(nèi)容或標(biāo)準(zhǔn)庫,以此優(yōu)化代碼書寫,需要的朋友可以參考下2022-09-09
Rust中的Iterator和IntoIterator介紹及應(yīng)用小結(jié)
Iterator即迭代器,它可以用于對數(shù)據(jù)結(jié)構(gòu)進(jìn)行迭代,被迭代的數(shù)據(jù)結(jié)構(gòu)是可迭代的(iterable),所謂的可迭代就是這個數(shù)據(jù)結(jié)構(gòu)有返回迭代器的方法,這篇文章主要介紹了Rust中的Iterator和IntoIterator介紹及應(yīng)用,需要的朋友可以參考下2023-07-07
解讀Rust的Rc<T>:實現(xiàn)多所有權(quán)的智能指針方式
Rc<T> 是 Rust 中用于多所有權(quán)的引用計數(shù)類型,通過增加引用計數(shù)來管理共享數(shù)據(jù),只有當(dāng)最后一個引用離開作用域時,數(shù)據(jù)才會被釋放,Rc<T> 適用于單線程環(huán)境,并且只允許不可變共享數(shù)據(jù);需要可變共享時應(yīng)考慮使用 RefCell<T> 或其他解決方案2025-02-02

