8个版本
0.1.4 | 2024年1月18日 |
---|---|
0.1.3 | 2024年1月18日 |
0.1.1 | 2023年4月21日 |
0.0.2 | 2023年3月20日 |
#82 in 压缩
165KB
3K SLoC
Archflow
一个用于一次性创建ZIP存档的库。当输出(如stdout或数据流)无法进行seek操作时,这非常有用。
ZIP是一种支持无损数据压缩的存档文件格式。ZIP文件可以包含一个或多个可能已压缩的文件或目录。ZIP文件格式允许使用多种压缩算法,尽管DEFLATE是最常用的。该格式最初于1989年创建,并在PKWARE,Inc.的PKZIP实用程序中首次实现。
当前实现基于
功能
- 一次性流式传输存档(即无seek)。
- tokio
AsyncRead
/AsyncWrite
和AsyncSeek
兼容。 - std::io
Read
/Write
和Seek
兼容 - 文件类型检测(文本或二进制)
- Zip64格式(存档大小超过4Gb)
- Unix时间
支持以下压缩格式
- 存储(即无压缩)
- deflate
- bzip2
- zstd
- xz
Cargo功能
在项目中包含时,您可以选择选择tokio(异步)或标准版本。
功能 | 描述 |
---|---|
tokio | 使用tokio非阻塞API,即: tokio::io::AsyncRead,tokio::io::AsyncWrite和tokio::io::AsyncSeek |
std | 使用标准API,即:std::io::Read,std::io::Write和std::io::Seek |
示例
文件系统
使用tokio::fs::File创建存档文件的简单示例
use archflow::{
compress::FileOptions, compress::tokio::archive::ZipArchive, compression::CompressionMethod,
error::ArchiveError,
};
use tokio::fs::File;
#[tokio::main]
async fn main() -> Result<(), ArchiveError> {
let file = File::create("archive.zip").await?;
let options = FileOptions::default().compression_method(CompressionMethod::Deflate());
let mut archive = ZipArchive::new_streamable(file);
archive.append("file1.txt", &options, &mut b"hello\n".as_ref()).await?;
let options = options.compression_method(CompressionMethod::Store());
archive.append("file2.txt", &options, &mut b"world\n".as_ref()).await?;
archive.finalize().await?;
Ok(())
}
Actix
将zip存档作为actix响应流式传输
use std::path::Path;
use actix_web::http::header::ContentDisposition;
use actix_web::{get, App, HttpResponse, HttpServer};
use archflow::compress::tokio::archive::ZipArchive;
use archflow::compress::FileOptions;
use archflow::compression::CompressionMethod;
use archflow::types::FileDateTime;
use tokio::fs::File;
use tokio::io::duplex;
use tokio_util::io::ReaderStream;
#[get("/zip")]
async fn zip_archive() -> HttpResponse {
let (w, r) = duplex(4096);
let options = FileOptions::default()
.last_modified_time(FileDateTime::Now)
.compression_method(CompressionMethod::Deflate());
tokio::spawn(async move {
let mut archive = ZipArchive::new_streamable(w);
let file_path = Path::new("./tests/resources/lorem_ipsum.txt");
let mut file = File::open(file_path).await.unwrap();
archive
.append("ipsum_deflate.txt", &options, &mut file)
.await
.unwrap();
archive
.append("file1.txt", &options, &mut b"world\n".as_ref())
.await
.unwrap();
archive
.append("file2.txt", &options, &mut b"world\n".as_ref())
.await
.unwrap();
let mut f = File::open(file_path).await.unwrap();
let options = options.compression_method(CompressionMethod::BZip2());
archive
.append("ipsum_bz.txt", &options, &mut f)
.await
.unwrap();
let mut f = File::open(file_path).await.unwrap();
let options = options.compression_method(CompressionMethod::Xz());
archive
.append("ipsum_xz.txt", &options, &mut f)
.await
.unwrap();
archive.finalize().await.unwrap();
});
HttpResponse::Ok()
.insert_header(("Content-Type", "application/zip"))
.insert_header(ContentDisposition::attachment("myzip.zip"))
.streaming(ReaderStream::new(r))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let address = "127.0.0.1";
let port = 8081;
println!("Test url is http://{}:{}/zip", address, port);
HttpServer::new(|| App::new().service(zip_archive))
.bind((address, port))?
.run()
.await
}
Hyper
将zip存档作为hyper响应流式传输
use archflow::{
compress::tokio::archive::ZipArchive, compress::FileOptions, compression::CompressionMethod,
types::FileDateTime,
};
use hyper::service::{make_service_fn, service_fn};
use hyper::{header, Body, Request, Response, Server, StatusCode};
use tokio::io::duplex;
use tokio_util::io::ReaderStream;
async fn zip_archive(_req: Request<Body>) -> Result<Response<Body>, hyper::http::Error> {
let (w, r) = duplex(4096);
let options = FileOptions::default()
.compression_method(CompressionMethod::Deflate())
.last_modified_time(FileDateTime::Now);
tokio::spawn(async move {
let mut archive = ZipArchive::new_streamable(w);
archive
.append("file1.txt", &options, &mut b"world\n".as_ref())
.await
.unwrap();
archive
.append("file2.txt", &options, &mut b"world\n".as_ref())
.await
.unwrap();
archive.finalize().await.unwrap();
});
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "application/zip")
.body(Body::wrap_stream(ReaderStream::new(r)))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let address = ([127, 0, 0, 1], 8081).into();
let service =
make_service_fn(|_| async { Ok::<_, hyper::http::Error>(service_fn(zip_archive)) });
let server = Server::bind(&address).serve(service);
println!("Listening on http://{}", address);
server.await?;
Ok(())
}
免责声明
该库正在建设中,且不稳定。需要做更多测试。
此实现受到以下启发:
Hyper
依赖项
~3.5–7MB
~142K SLoC