diff --git a/image.jpg b/image.jpg new file mode 100644 index 0000000..8ea97ed Binary files /dev/null and b/image.jpg differ diff --git a/lib/cache/image.ts b/lib/cache/image.ts index 3925f8b..b6993f6 100644 --- a/lib/cache/image.ts +++ b/lib/cache/image.ts @@ -1,5 +1,6 @@ import { hash } from "@lib/hash.ts"; import * as cache from "@lib/cache/cache.ts"; +import { ImageMagick } from "https://deno.land/x/imagemagick_deno@0.0.25/mod.ts"; type ImageCacheOptions = { url: string; @@ -19,6 +20,20 @@ function getCacheKey({ url: _url, width, height }: ImageCacheOptions) { ); } +function verifyImage( + imageBuffer: Uint8Array, +) { + return new Promise((resolve) => { + try { + ImageMagick.read(imageBuffer, (image) => { + resolve(image.height !== 0 && image.width !== 0); + }); + } catch (_err) { + resolve(false); + } + }); +} + export async function getImage({ url, width, height }: ImageCacheOptions) { const cacheKey = getCacheKey({ url, width, height }); @@ -44,6 +59,12 @@ export async function setImage( ) { const clone = new Uint8Array(buffer); + const imageCorrect = await verifyImage(clone); + if (!imageCorrect) { + console.log("[cache/image] failed to store image", { url }); + return; + } + const cacheKey = getCacheKey({ url, width, height }); const pointerId = await hash(cacheKey); diff --git a/routes/api/images/index.ts b/routes/api/images/index.ts index 64ac416..bc6a922 100644 --- a/routes/api/images/index.ts +++ b/routes/api/images/index.ts @@ -2,8 +2,10 @@ import { HandlerContext, Handlers } from "$fresh/server.ts"; import { ImageMagick, initialize, + MagickFormat, MagickGeometry, } from "https://deno.land/x/imagemagick_deno@0.0.25/mod.ts"; + import { parseMediaType } from "https://deno.land/std@0.175.0/media_types/parse_media_type.ts"; import * as cache from "@lib/cache/image.ts"; import { SILVERBULLET_SERVER } from "@lib/env.ts"; @@ -49,10 +51,21 @@ function getWidthHeight( function modifyImage( imageBuffer: Uint8Array, - params: { width: number; height: number; mode: "resize" | "crop" }, + params: { + width: number; + mediaType: string; + height: number; + mode: "resize" | "crop"; + }, ) { return new Promise((resolve) => { - ImageMagick.read(imageBuffer, (image) => { + let format = MagickFormat.Jpeg; + + if (params.mediaType === "image/webp") { + format = MagickFormat.Webp; + } + + ImageMagick.read(imageBuffer, format, (image) => { const sizingData = getWidthHeight(image, params); if (params.mode === "resize") { image.resize(sizingData); @@ -100,6 +113,7 @@ async function processImage(imageUrl: string, params: ImageParams) { const modifiedImage = await modifyImage(remoteImage.buffer, { ...params, + mediaType: remoteImage.mediaType, mode: "resize", }); @@ -108,13 +122,6 @@ async function processImage(imageUrl: string, params: ImageParams) { length: modifiedImage.length, }); - await cache.setImage(modifiedImage.slice(), { - url: imageUrl, - width: params.width, - height: params.height, - mediaType: remoteImage.mediaType, - }); - return [modifiedImage, remoteImage.mediaType] as const; } @@ -131,15 +138,13 @@ const GET = async ( const imageUrl = SILVERBULLET_SERVER + "/" + params.image; - console.log("[api/image] " + imageUrl); - const cachedResponse = await cache.getImage({ url: imageUrl, width: params.width, height: params.height, }); if (cachedResponse) { - console.log("[api/image] got cached image"); + console.log("[api/image] cached: " + imageUrl); return new Response(cachedResponse.buffer.slice(), { headers: { "Content-Type": cachedResponse.mediaType, @@ -153,6 +158,15 @@ const GET = async ( processImage(imageUrl, params) ); + cache.setImage(resizedImage.slice(), { + url: imageUrl, + width: params.width, + height: params.height, + mediaType: mediaType, + }); + + console.log("[api/image] not-cached: " + imageUrl); + return new Response(resizedImage, { headers: { "Content-Type": mediaType,