RAW: try IFD0 + IFD1 for embedded preview, serve at full size
The thumbnail pipeline's embedded-JPEG extractor only checked IFD1 (THUMBNAIL), which on many Nikon NEFs is missing or zero-length even when IFD0 (PRIMARY) carries a perfectly good 1-2 MP reduced-resolution preview the camera writes for in-body review. The previous behavior produced black thumbs on disk: the buggy IFD1 pointer resolved to a short byte sequence that happened to satisfy the SOI sanity check, image::load_from_memory accepted it, and the resize path quietly wrote a black JPEG. Now both IFDs are checked and the larger valid JPEG wins. Format- agnostic: applies to every TIFF-based RAW (NEF / ARW / CR2 / DNG / RAF / ORF / RW2 / PEF / SRW / TIFF). is_tiff_raw is now pub so main.rs can gate its full-size handler on it. Also extends the /image handler so size=full requests for RAW formats serve the embedded preview as image/jpeg instead of NamedFile-streaming the original RAW bytes - browsers can't decode a .nef container, so <img src=...> would otherwise land as a broken image. Falls through to NamedFile if no preview is present, preserving the historical behavior for callers that genuinely want the original bytes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
17
src/main.rs
17
src/main.rs
@@ -215,6 +215,23 @@ async fn get_image(
|
||||
}
|
||||
}
|
||||
|
||||
// Full-size requests for RAW formats (NEF/CR2/ARW/etc.) can't just
|
||||
// NamedFile-stream the original bytes — browsers won't decode the
|
||||
// RAW container, so a `<img src=...>` lands as a broken image. Serve
|
||||
// the embedded JPEG preview instead (typically the camera's in-body
|
||||
// review JPEG, ~1–2 MP). Falls through to NamedFile if no preview is
|
||||
// available, which preserves the historical behavior for callers
|
||||
// that genuinely want the original bytes.
|
||||
if image_size == PhotoSize::Full && exif::is_tiff_raw(&path) {
|
||||
if let Some(preview) = exif::extract_embedded_jpeg_preview(&path) {
|
||||
span.set_status(Status::Ok);
|
||||
return HttpResponse::Ok()
|
||||
.content_type("image/jpeg")
|
||||
.insert_header(("Cache-Control", "public, max-age=3600"))
|
||||
.body(preview);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(file) = NamedFile::open(&path) {
|
||||
span.set_status(Status::Ok);
|
||||
// Enable ETag and set cache headers for full images (1 hour cache)
|
||||
|
||||
Reference in New Issue
Block a user