Blossom: Determine mime type and supply extension in Blob Descriptor URL

This commit is contained in:
Mike Dilger 2024-12-10 09:16:33 +13:00
parent a4544702b5
commit 3cacc4e963
No known key found for this signature in database
GPG Key ID: 47581A78D4329BA4
4 changed files with 77 additions and 7 deletions

36
Cargo.lock generated
View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "addr2line" name = "addr2line"
@ -278,6 +278,8 @@ dependencies = [
"hyper-util", "hyper-util",
"lazy_static", "lazy_static",
"log", "log",
"mime-sniffer",
"new_mime_guess",
"parking_lot", "parking_lot",
"pocket-db", "pocket-db",
"pocket-types", "pocket-types",
@ -1094,6 +1096,22 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime-sniffer"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b8b2a64cd735f1d5f17ff6701ced3cc3c54851f9448caf454cd9c923d812408"
dependencies = [
"mime",
"url",
]
[[package]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.2.1" version = "0.2.1"
@ -1137,6 +1155,16 @@ dependencies = [
"memmap2", "memmap2",
] ]
[[package]]
name = "new_mime_guess"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02a2dfb3559d53e90b709376af1c379462f7fb3085a0177deb73e6ea0d99eff4"
dependencies = [
"mime",
"unicase",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -1931,6 +1959,12 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicase"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"

View File

@ -21,6 +21,8 @@ hyper-tungstenite = "0.15"
hyper-util = "0.1" hyper-util = "0.1"
lazy_static = "1.5" lazy_static = "1.5"
log = "0.4" log = "0.4"
mime-sniffer = "0.1"
new_mime_guess = "4.0"
pocket-types = { git = "https://github.com/mikedilger/pocket", branch = "master" } pocket-types = { git = "https://github.com/mikedilger/pocket", branch = "master" }
pocket-db = { git = "https://github.com/mikedilger/pocket", branch = "master" } pocket-db = { git = "https://github.com/mikedilger/pocket", branch = "master" }
parking_lot = "0.12" parking_lot = "0.12"

View File

@ -48,7 +48,7 @@ impl FileStore {
&self, &self,
data: BoxBody<Bytes, Error>, data: BoxBody<Bytes, Error>,
expected_hash: Option<HashOutput>, expected_hash: Option<HashOutput>,
) -> Result<(u64, HashOutput), Error> { ) -> Result<(u64, HashOutput, Option<String>), Error> {
use bitcoin_hashes::sha256; use bitcoin_hashes::sha256;
use std::io::Write; // for hash_engine.write_all() use std::io::Write; // for hash_engine.write_all()
@ -78,6 +78,7 @@ impl FileStore {
// Copy the data into the tempfile (hashing and counting as we go) // Copy the data into the tempfile (hashing and counting as we go)
let count = tokio::io::copy(&mut inspect_reader, &mut tempfile).await?; let count = tokio::io::copy(&mut inspect_reader, &mut tempfile).await?;
drop(tempfile);
// Verify our code was correct // Verify our code was correct
if count != size { if count != size {
@ -101,6 +102,16 @@ impl FileStore {
} }
} }
// Sniff the mime-type
let maybe_mime_string = {
use mime_sniffer::MimeTypeSniffer;
use tokio::io::AsyncReadExt;
let mut readtempfile = File::open(&temppathbuf).await?;
let mut buffer: Vec<u8> = vec![0; 128];
let _ = readtempfile.read(&mut buffer).await?;
buffer.sniff_mime_type().map(|s| s.to_string())
};
// Compute the proper path // Compute the proper path
let pathbuf = hash.to_pathbuf(&self.base); let pathbuf = hash.to_pathbuf(&self.base);
@ -109,7 +120,7 @@ impl FileStore {
// Just clean up // Just clean up
fs::remove_file(&temppathbuf).await?; fs::remove_file(&temppathbuf).await?;
return Ok((size, hash)); return Ok((size, hash, maybe_mime_string));
} }
// Make the parent directory // Make the parent directory
@ -118,7 +129,7 @@ impl FileStore {
// Move the file // Move the file
fs::rename(&temppathbuf, &pathbuf).await?; fs::rename(&temppathbuf, &pathbuf).await?;
Ok((size, hash)) Ok((size, hash, maybe_mime_string))
} }
/// Retrieve a file from storage by its HashOutput, streamed to a hyper BoxBoxy /// Retrieve a file from storage by its HashOutput, streamed to a hyper BoxBoxy

View File

@ -229,7 +229,15 @@ pub async fn handle_upload(
let uri = request.uri().to_owned(); let uri = request.uri().to_owned();
let (size, hash) = GLOBALS let maybe_content_type = match request.headers().get(http::header::CONTENT_TYPE) {
Some(s) => match s.to_str() {
Ok(s) => Some(s.to_owned()),
Err(_) => None,
},
None => None,
};
let (size, hash, maybe_sniffed_mime_string) = GLOBALS
.filestore .filestore
.get() .get()
.unwrap() .unwrap()
@ -239,11 +247,26 @@ pub async fn handle_upload(
) )
.await?; .await?;
let extension = {
let mut mime_string: String = "".to_owned();
if let Some(ms) = maybe_content_type {
mime_string = ms.to_owned();
} else if let Some(ms) = maybe_sniffed_mime_string {
mime_string = ms.to_owned();
}
if let Some(exts) = new_mime_guess::get_mime_extensions_str(&mime_string) {
exts[0]
} else {
"blob"
}
};
let uri = { let uri = {
let mut parts = GLOBALS.config.read().uri_parts(uri, true)?; let mut parts = GLOBALS.config.read().uri_parts(uri, true)?;
parts.path_and_query = Some(http::uri::PathAndQuery::from_maybe_shared(format!( parts.path_and_query = Some(http::uri::PathAndQuery::from_maybe_shared(format!(
"/{}", "/{}.{}",
hash hash, extension
))?); ))?);
http::Uri::from_parts(parts)? http::Uri::from_parts(parts)?
}; };