Zip files
Zip files made from several files and opened safely: nothing written outside the folder, no zip bombs.
Version 1.0.0 · 3 functions · 1 crate
Functions
What use archive.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use zip_files, zip_list from archive (Quillo 0.6).
zip_files(to: str, files: list, base: str) → intor an errorA zip at to of files, paths relative to base (kept so inside the zip); its size in bytes.
zip_list(path: str) → listor an errorWhat a zip holds: [{ name, size, packed, folder }], sizes in bytes.
unzip(from: str, to: str) → listor an errorUnpacks the zip at from into the folder to (made if needed); the names written. Paths that leave the folder are refused, and so is a zip over 1 GiB unpacked.
Get it
$ quillo install archive # archive.qwer into this folder$ quillo install archive --example # and its example, archive.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/archive.qwer$ curl -O https://quillolang.org/plugins/files/archive.qweThen, in your program, all of it or only what you call:
use archive.qweruse zip_files, zip_list from archive // Quillo 0.6: only theseIt uses this crate, which cargo fetches the first time the plugin is built:
zip = { version = "2", default-features = false, features = ["deflate"] }Example
// An export of several files as one download, and the zip read back.//// quillo run archive.qwe then open http://localhost:8080/exportuse "archive.qwer"route GET /export { write_file("export/orders.csv", "id,total\n1,120.50\n") write_file("export/customers.csv", "id,name\n7,Ada\n") zip_files("export.zip", ["orders.csv", "customers.csv"], "export")? { files: zip_list("export.zip")?, unpacked: unzip("export.zip", "unpacked")? }}route GET /export.zip = file("export.zip")$ quillo run archive.qwe$ curl 'localhost:8080/export'Source
//! Zip files made and opened: exports of several files, uploads of a folder, backups. Unpacking never writes outside its folder.//! version: 1.0.0//!//! [dependencies]//! zip = { version = "2", default-features = false, features = ["deflate"] }use serde_json::{json, Value};use std::fs::File;use std::path::Path;/// Larger than this unpacked, a zip is refused: a small file can unpack to gigabytes.const MAX_UNPACKED: u64 = 1 << 30;/// A zip at `to` of `files`, paths relative to `base` (kept so inside the zip); its size in bytes.pub fn zip_files(to: &str, files: Vec<String>, base: &str) -> Result<i64, String> { let mut z = zip::ZipWriter::new(File::create(to).map_err(|e| format!("{}: {}", to, e))?); let opts = zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated); for f in &files { let full = Path::new(base).join(f); z.start_file(f.replace('\\', "/"), opts).map_err(|e| e.to_string())?; let mut src = File::open(&full).map_err(|e| format!("{}: {}", full.display(), e))?; std::io::copy(&mut src, &mut z).map_err(|e| e.to_string())?; } z.finish().map_err(|e| e.to_string())?; Ok(std::fs::metadata(to).map_err(|e| e.to_string())?.len() as i64)}/// What a zip holds: `[{ name, size, packed, folder }]`, sizes in bytes.pub fn zip_list(path: &str) -> Result<Vec<Value>, String> { let mut a = zip::ZipArchive::new(File::open(path).map_err(|e| format!("{}: {}", path, e))?).map_err(|e| e.to_string())?; (0..a.len()).map(|i| { let e = a.by_index(i).map_err(|e| e.to_string())?; Ok(json!({ "name": e.name(), "size": e.size(), "packed": e.compressed_size(), "folder": e.is_dir() })) }).collect()}/// Unpacks the zip at `from` into the folder `to` (made if needed); the names written. Paths that leave the folder are refused, and so is a zip over 1 GiB unpacked.pub fn unzip(from: &str, to: &str) -> Result<Vec<String>, String> { let mut a = zip::ZipArchive::new(File::open(from).map_err(|e| format!("{}: {}", from, e))?).map_err(|e| e.to_string())?; let mut total = 0u64; for i in 0..a.len() { let e = a.by_index(i).map_err(|e| e.to_string())?; if e.enclosed_name().is_none() { return Err(format!("`{}` would be written outside the folder", e.name())); } total += e.size(); } if total > MAX_UNPACKED { return Err(format!("the zip unpacks to {} bytes, over the 1 GiB allowed", total)); } std::fs::create_dir_all(to).map_err(|e| e.to_string())?; a.extract(to).map_err(|e| e.to_string())?; Ok(a.file_names().map(str::to_string).collect())}Related
models_3d.qwer · 1.0.0STL and OBJ models read: triangles, size, volume, surface area and whether the mesh is closed, a 3D-print quote by weight, and OBJ turned into binary STL.6 views · 7 downloadsbzip2 bzip2.qwer · 1.0.0bzip2 compression of files and text in pure Rust, .bz2 dumps unpacked, and their first lines read without unpacking them all.6 views · 7 downloadsDisk space disk_space.qwer · 1.0.0Free and total space of the disk a path is on, for health checks, refusing uploads before the disk fills, and admin pages.8 views · 8 downloadsFile type file_type.qwer · 1.0.0What a file really is from its first bytes, not its name: MIME type, extension and kind, for upload checks.7 views · 9 downloadsFonts fonts.qwer · 1.0.0A font file's names, weight and style, glyph count, which characters it covers, and how wide a text is set in it.5 views · 7 downloadsMIME types mime.qwer · 1.0.0MIME types from file names and extensions, the extensions of a type, and Content-Type headers with a charset for text.8 views · 7 downloadsThread 0 posts
On Zip files: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.