Stream ZIP archives directly from your Axum handlers.
axum-zip-response
provides a ZipResponse
type that lets you build a ZIP
archive on the fly and stream it to the client. Files are compressed and sent as
soon as they are added, making it possible to serve large archives without
holding everything in memory.
- Incrementally add files while the response is streamed.
- Optional
chrono
feature to set ZIP entry modification times. - Automatically stops writing if the client disconnects.
Add the crate to your Cargo.toml
:
axum-zip-response = "0.1"
Create a ZIP archive in an Axum handler:
use axum::response::IntoResponse;
use axum_zip_response::{ZipResponse, ZipResponseError};
async fn download_zip() -> Result<impl IntoResponse, ZipResponseError> {
let (response, mut writer) = ZipResponse::new();
writer.add_file("hello.txt", b"Hello, World!\n").await?;
writer.add_file(
"example.txt",
b"This is an example file.\n",
).await?;
writer.finish().await?;
Ok(response)
}
You can also populate the archive from another task:
use axum_extra::response::Attachment;
async fn stream_zip() -> Result<impl IntoResponse, ZipResponseError> {
let (response, mut writer) = ZipResponse::new();
tokio::spawn(async move {
for i in 0..10 {
let name = format!("file_{i}.txt");
let content = format!("This is the content of file {i}.\n");
writer.add_file(&name, content.as_bytes()).await.unwrap();
}
writer.finish().await.unwrap();
});
// By returning a [`axum_extra::response::Attachment`] we can specify a filename
Ok(Attachment::new(response.into_body())
.filename("archive.zip")
.content_type("application/zip"))
}