用户钟声洪 發表於 2024-4-12 11:06:15

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

<blockquote><p>在使用actix-web框架的时候,如果使用zip解压任务将会占用一个工作线程,因为zip库是同步阻塞的,想用异步非阻塞需要用另一个库,下面列出同步解压,跟异步解压的两个方法实现,异步解压不会占用工作线程。注意:debug模式下rust异步压缩会很慢,打包成release之后就非常快了。</p></blockquote>
<h2>压缩</h2>
<p>依赖</p>
<div class="jb51code"><pre class="brush:plain;">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"</pre></div>
<p>rust代码</p>
<div class="jb51code"><pre class="brush:plain;">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) -&gt; Result&lt;Vec&lt;PathBuf&gt;, anyhow::Error&gt; {
    let mut dirs = vec!;
    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: &amp;Path,
    file_name: &amp;str,
    zip_writer: &amp;mut ZipFileWriter&lt;File&gt;,
) -&gt; Result&lt;(), anyhow::Error&gt; {
    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(&amp;mut input_file.compat(), &amp;mut entry_writer).await?;
    entry_writer.close().await?;
    return Ok(());
}
//压缩
pub async fn zip(input_path: &amp;Path, out_path: &amp;Path) -&gt; Result&lt;(), anyhow::Error&gt; {
    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, &amp;file_name, &amp;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 = &amp;entry_str[(input_dir_str.len() + 1)..];
            zip_entry(entry_path, file_name, &amp;mut writer).await?;
      }
    }
    writer.close().await?;
    Ok(())
}
//解压
pub async fn unzip&lt;T: AsRef&lt;Path&gt;&gt;(path: T, out_path: T) -&gt; Result&lt;(), anyhow::Error&gt; {
    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 = &amp;String::from_utf8_lossy(raw).to_string(); //必需转换为utf8,否则有乱码
      let zip_path = out_path.join(file_name);
      if file_name.ends_with("/") {
            create_dir_all(&amp;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(&amp;zip_path)
            .await?;
      futures_lite::io::copy(&amp;mut entry_reader, &amp;mut writer.compat_write()).await?;
    }
    Ok(())
}</pre></div>
<p>测试</p>
<div class="jb51code"><pre class="brush:plain;">#
mod tests {
    use super::*;
    #
    async fn test_zip() -&gt; Result&lt;(), anyhow::Error&gt; {
      let path = Path::new("file/tmp/test");
      zip(path, Path::new("file/tmp/out.zip")).await?;
      Ok(())
    }
    #
    async fn test_unzip() -&gt; Result&lt;(), anyhow::Error&gt; {
      let path = Path::new("file/tmp/a/out.zip");
      unzip(path, Path::new("file/tmp")).await?;
      Ok(())
    }
}</pre></div>
<p>到此这篇关于rust zip异步压缩与解压的文章就介绍到这了,更多相关rust zip解压内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>改进 JavaScript 和 Rust 的互操作性并深入认识 wasm-bindgen 组件</li><li>如何使用python wasmtime调用rust生成的wasm库</li><li>前端基于Rust实现的Wasm进行图片压缩的技术文档(实现方案)</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: rust zip异步压缩与解压的代码详解