rust zip异步压缩与解压的代码详解

 更新时间:2024年04月12日 11:06:15   作者: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解压内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Rust中的Box<T>之堆上的数据与递归类型详解

    Rust中的Box<T>之堆上的数据与递归类型详解

    本文介绍了Rust中的Box<T>类型,包括其在堆与栈之间的内存分配,性能优势,以及如何利用Box<T>来实现递归类型和处理大小未知类型,通过Box<T>,Rust程序员可以更灵活地管理内存,避免编译时大小不确定的问题,并提高代码的效率和灵活性
    2025-02-02
  • Rust cargo 命令行工具使用示例教程

    Rust cargo 命令行工具使用示例教程

    cargo 是 Rust 的构建系统和包管理器,负责创建项目、编译代码、管理依赖、运行测试等,是日常开发中最常用的工具,这篇文章主要介绍了Rust cargo 命令行工具使用教程,需要的朋友可以参考下
    2025-04-04
  • Rust并发编程之使用消息传递进行线程间数据共享方式

    Rust并发编程之使用消息传递进行线程间数据共享方式

    文章介绍了Rust中的通道(channel)概念,包括通道的基本概念、创建并使用通道、通道与所有权、发送多个消息以及多发送端,通道提供了一种线程间安全的通信机制,通过所有权规则确保数据安全,并且支持多生产者单消费者架构
    2025-02-02
  • Rust指南之泛型与特性详解

    Rust指南之泛型与特性详解

    泛型机制是编程语言用于表达类型抽象的机制,一般用于功能确定、数据类型待定的类,如链表、映射表等,这篇文章主要介绍了Rust指南泛型与特性,需要的朋友可以参考下
    2022-10-10
  • Rust的slab库使用场景分析

    Rust的slab库使用场景分析

    slab 是一个轻量级、高性能的工具,非常适合管理固定大小的资源集合,尤其是在网络编程和事件驱动架构中,这篇文章主要介绍了Rust的slab库使用教程,需要的朋友可以参考下
    2024-12-12
  • 使用cargo install安装Rust二进制工具过程

    使用cargo install安装Rust二进制工具过程

    cargoinstall是一个用于安装包含可执行目标的Rust包的命令行工具,类似于系统软件包管理器,但它为Rust开发者提供了一种简洁的方式来安装和管理命令行工具,安装后,二进制文件会存储在$HOME/.cargo/bin目录中,需要将该目录添加到$PATH环境变量中才能在命令行中直接运行
    2025-02-02
  • Rust 能够取代 C 语言吗

    Rust 能够取代 C 语言吗

    Rust 是 Mozilla 基金会的一个雄心勃勃的项目,号称是 C 语言和 C++ 的继任者,这篇文章主要介绍了Rust 能够取代 C 语言吗的相关知识,需要的朋友可以参考下
    2020-06-06
  • rust的nutyp验证和validator验证数据的方法示例详解

    rust的nutyp验证和validator验证数据的方法示例详解

    本文介绍了在Rust语言中,如何使用nuType和validator两种工具来对Cargo.toml和modules.rs文件进行验证,通过具体的代码示例和操作步骤,详细解释了验证过程和相关配置,帮助读者更好地理解和掌握使用这两种验证工具的方法,更多Rust相关技术资讯,可继续关注脚本之家
    2024-09-09
  • libbpf和Rust开发ebpf程序实战示例

    libbpf和Rust开发ebpf程序实战示例

    这篇文章主要为大家介绍了libbpf和Rust开发ebpf程序实战示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 一文弄懂Rust之切片

    一文弄懂Rust之切片

    在Rust中,切片是一种非常重要的引用类型,它允许你安全地引用一段连续内存中的数据,而不需要拥有这些数据的所有权,本文主要介绍了Rust之切片,感兴趣的可以了解一下
    2024-03-03

最新评论