Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/node-libraries/wasm-image-optimization
Optimize images with wasm on edge runtime
https://github.com/node-libraries/wasm-image-optimization
Last synced: 4 days ago
JSON representation
Optimize images with wasm on edge runtime
- Host: GitHub
- URL: https://github.com/node-libraries/wasm-image-optimization
- Owner: node-libraries
- Created: 2023-12-11T05:20:16.000Z (about 1 year ago)
- Default Branch: master
- Last Pushed: 2024-12-01T07:42:39.000Z (about 2 months ago)
- Last Synced: 2025-01-13T11:07:20.736Z (12 days ago)
- Language: C++
- Size: 521 KB
- Stars: 69
- Watchers: 1
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# wasm-image-optimization
- Cloudflare workers
`import { optimizeImage } from 'wasm-image-optimization';`
- Next.js(Webpack)
`import { optimizeImage } from 'wasm-image-optimization/next';`
- ESM(import base) & Deno Deploy
`import { optimizeImage } from 'wasm-image-optimization/esm';`
- CJS(file base)
`import { optimizeImage } from 'wasm-image-optimization/node';`- function
```ts
// quality: 1-100
optimizeImage({image: ArrayBuffer, width?: number, height?:number,quality?: number,format?: "png" | "jpeg" | "avif" | "webp"}): PromiseoptimizeImageExt({image: ArrayBuffer, width?: number, height?:number,quality?: number,format?: "png" | "jpeg" | "avif" | "webp"}): Promise<{data: Uint8Array;originalWidth: number;originalHeight: number; width: number;height: number;}>
```
- source format
- svg
- jpeg(EXIF orientation is supported)
- png
- webp
- avif
- output format
- jpeg
- png
- webp
- avif(default)# usage
## Next.js image optimization with Cloudflare
```ts
import { optimizeImage } from "wasm-image-optimization";const isValidUrl = (url: string) => {
try {
new URL(url);
return true;
} catch (err) {
return false;
}
};const handleRequest = async (
request: Request,
_env: {},
ctx: ExecutionContext
): Promise => {
const accept = request.headers.get("accept");
const isWebp =
accept
?.split(",")
.map((format) => format.trim())
.some((format) => ["image/webp", "*/*", "image/*"].includes(format)) ??
true;
const isAvif =
accept
?.split(",")
.map((format) => format.trim())
.some((format) => ["image/avif", "*/*", "image/*"].includes(format)) ??
true;const url = new URL(request.url);
const params = url.searchParams;
const imageUrl = params.get("url");
if (!imageUrl || !isValidUrl(imageUrl)) {
return new Response("url is required", { status: 400 });
}const cache = caches.default;
url.searchParams.append("webp", isWebp.toString());
const cacheKey = new Request(url.toString());
const cachedResponse = await cache.match(cacheKey);
if (cachedResponse) {
return cachedResponse;
}const width = params.get("w");
const quality = params.get("q");const [srcImage, contentType] = await fetch(imageUrl, {
cf: { cacheKey: imageUrl },
})
.then(async (res) =>
res.ok
? ([await res.arrayBuffer(), res.headers.get("content-type")] as const)
: []
)
.catch(() => []);if (!srcImage) {
return new Response("image not found", { status: 404 });
}if (contentType && ["image/svg+xml", "image/gif"].includes(contentType)) {
const response = new Response(srcImage, {
headers: {
"Content-Type": contentType,
"Cache-Control": "public, max-age=31536000, immutable",
},
});
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}const format = isAvif
? "avif"
: isWebp
? "webp"
: contentType === "image/jpeg"
? "jpeg"
: "png";
const image = await optimizeImage({
image: srcImage,
width: width ? parseInt(width) : undefined,
quality: quality ? parseInt(quality) : undefined,
format,
});
const response = new Response(image, {
headers: {
"Content-Type": `image/${format}`,
"Cache-Control": "public, max-age=31536000, immutable",
date: new Date().toUTCString(),
},
});
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
};export default {
fetch: handleRequest,
};
```- next.config.js
```js
/**
* @type { import("next").NextConfig}
*/
const config = {
images: {
path: "https://xxx.yyy.workers.dev/",
},
};
export default config;
```## Deno Deploy
### Parameters
| Name | Type | Description |
| ---- | ------ | ----------- |
| url | string | Image URL |
| q | number | Quality |
| w | number | Width |https://xxxx.deno.dev/?url=https://xxx.png&q=80&w=200
```ts
import { optimizeImage } from "npm:wasm-image-optimization/esm";const isValidUrl = (url: string) => {
try {
new URL(url);
return true;
} catch (_e) {
return false;
}
};const isType = (accept: string | null, type: string) => {
return (
accept
?.split(",")
.map((format) => format.trim())
.some((format) => [`image/${type}`, "*/*", "image/*"].includes(format)) ??
true
);
};Deno.serve(async (request) => {
const url = new URL(request.url);
const params = url.searchParams;
const type = ["avif", "webp", "png", "jpeg"].find(
(v) => v === params.get("type")
) as "avif" | "webp" | "png" | "jpeg" | undefined;
const accept = request.headers.get("accept");
const isAvif = isType(accept, "avif");
const isWebp = isType(accept, "webp");const cache = await caches.open(
`image-${isAvif ? "-avif" : ""}${isWebp ? "-webp" : ""}`
);const cached = await cache.match(request);
if (cached) {
return cached;
}const imageUrl = params.get("url");
if (!imageUrl || !isValidUrl(imageUrl)) {
return new Response("url is required", { status: 400 });
}if (isAvif) {
url.searchParams.append("avif", isAvif.toString());
} else if (isWebp) {
url.searchParams.append("webp", isWebp.toString());
}const cacheKey = new Request(url.toString());
const cachedResponse = await cache.match(cacheKey);
if (cachedResponse) {
return cachedResponse;
}const width = params.get("w");
const quality = params.get("q");const [srcImage, contentType] = await fetch(imageUrl)
.then(async (res) =>
res.ok
? ([await res.arrayBuffer(), res.headers.get("content-type")] as const)
: []
)
.catch(() => []);if (!srcImage) {
return new Response("image not found", { status: 404 });
}if (contentType && ["image/svg+xml", "image/gif"].includes(contentType)) {
const response = new Response(srcImage, {
headers: {
"Content-Type": contentType,
"Cache-Control": "public, max-age=31536000, immutable",
},
});
cache.put(request, response.clone());
return response;
}const format =
type ??
(isAvif
? "avif"
: isWebp
? "webp"
: contentType === "image/jpeg"
? "jpeg"
: "png");
const image = await optimizeImage({
image: srcImage,
width: width ? parseInt(width) : undefined,
quality: quality ? parseInt(quality) : undefined,
format,
});
const response = new Response(image, {
headers: {
"Content-Type": `image/${format}`,
"Cache-Control": "public, max-age=31536000, immutable",
date: new Date().toUTCString(),
},
});
cache.put(request, response.clone());
return response;
});
```