wasm-image-optimization 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# wasm-image-optimization
|
|
2
2
|
|
|
3
|
+
- The 'avif' version is available here
|
|
4
|
+
https://www.npmjs.com/package/wasm-image-optimization-avif
|
|
5
|
+
|
|
3
6
|
- Cloudflare workers
|
|
4
7
|
`import { optimizeImage } from 'wasm-image-optimization';`
|
|
5
8
|
- Next.js(Webpack)
|
|
@@ -31,8 +34,23 @@ optimizeImage({image: ArrayBuffer, width?: number, height?:number,quality?: numb
|
|
|
31
34
|
|
|
32
35
|
## Next.js image optimization with Cloudflare
|
|
33
36
|
|
|
37
|
+
### Parameters
|
|
38
|
+
|
|
39
|
+
| Name | Type | Description |
|
|
40
|
+
| ---- | ------ | -------------------------- |
|
|
41
|
+
| url | string | Image URL |
|
|
42
|
+
| q | number | Quality |
|
|
43
|
+
| w | number | Width |
|
|
44
|
+
| type | string | Output type(webp/png/jpeg) |
|
|
45
|
+
|
|
46
|
+
https://xxx.yyy.workers.dev/?url=https://xxx.png&q=80&w=200
|
|
47
|
+
|
|
48
|
+
### Source code
|
|
49
|
+
|
|
50
|
+
https://github.com/SoraKumo001/cloudflare-workers-image-optimization
|
|
51
|
+
|
|
34
52
|
```ts
|
|
35
|
-
import { optimizeImage } from
|
|
53
|
+
import { optimizeImage } from "wasm-image-optimization";
|
|
36
54
|
|
|
37
55
|
const isValidUrl = (url: string) => {
|
|
38
56
|
try {
|
|
@@ -48,54 +66,64 @@ const handleRequest = async (
|
|
|
48
66
|
_env: {},
|
|
49
67
|
ctx: ExecutionContext
|
|
50
68
|
): Promise<Response> => {
|
|
51
|
-
const
|
|
69
|
+
const url = new URL(request.url);
|
|
70
|
+
const params = url.searchParams;
|
|
71
|
+
const type = ["webp", "png", "jpeg"].find((v) => v === params.get("type")) as
|
|
72
|
+
| "webp"
|
|
73
|
+
| "png"
|
|
74
|
+
| "jpeg"
|
|
75
|
+
| undefined;
|
|
76
|
+
const accept = request.headers.get("accept");
|
|
52
77
|
const isWebp =
|
|
53
78
|
accept
|
|
54
|
-
?.split(
|
|
79
|
+
?.split(",")
|
|
55
80
|
.map((format) => format.trim())
|
|
56
|
-
.some((format) => [
|
|
57
|
-
|
|
58
|
-
const url = new URL(request.url);
|
|
81
|
+
.some((format) => ["image/webp", "*/*", "image/*"].includes(format)) ??
|
|
82
|
+
true;
|
|
59
83
|
|
|
60
|
-
const
|
|
61
|
-
const imageUrl = params.get('url');
|
|
84
|
+
const imageUrl = params.get("url");
|
|
62
85
|
if (!imageUrl || !isValidUrl(imageUrl)) {
|
|
63
|
-
return new Response(
|
|
86
|
+
return new Response("url is required", { status: 400 });
|
|
64
87
|
}
|
|
65
88
|
|
|
66
89
|
const cache = caches.default;
|
|
67
|
-
url.searchParams.append(
|
|
90
|
+
url.searchParams.append("webp", isWebp.toString());
|
|
68
91
|
const cacheKey = new Request(url.toString());
|
|
69
92
|
const cachedResponse = await cache.match(cacheKey);
|
|
70
93
|
if (cachedResponse) {
|
|
71
94
|
return cachedResponse;
|
|
72
95
|
}
|
|
73
96
|
|
|
74
|
-
const width = params.get(
|
|
75
|
-
const quality = params.get(
|
|
97
|
+
const width = params.get("w");
|
|
98
|
+
const quality = params.get("q");
|
|
76
99
|
|
|
77
|
-
const [srcImage, contentType] = await fetch(imageUrl, {
|
|
100
|
+
const [srcImage, contentType] = await fetch(imageUrl, {
|
|
101
|
+
cf: { cacheKey: imageUrl },
|
|
102
|
+
})
|
|
78
103
|
.then(async (res) =>
|
|
79
|
-
res.ok
|
|
104
|
+
res.ok
|
|
105
|
+
? ([await res.arrayBuffer(), res.headers.get("content-type")] as const)
|
|
106
|
+
: []
|
|
80
107
|
)
|
|
81
108
|
.catch(() => []);
|
|
82
109
|
|
|
83
110
|
if (!srcImage) {
|
|
84
|
-
return new Response(
|
|
111
|
+
return new Response("image not found", { status: 404 });
|
|
85
112
|
}
|
|
86
113
|
|
|
87
|
-
if (contentType && [
|
|
114
|
+
if (contentType && ["image/svg+xml", "image/gif"].includes(contentType)) {
|
|
88
115
|
const response = new Response(srcImage, {
|
|
89
116
|
headers: {
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
"Content-Type": contentType,
|
|
118
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
92
119
|
},
|
|
93
120
|
});
|
|
94
121
|
ctx.waitUntil(cache.put(cacheKey, response.clone()));
|
|
95
122
|
return response;
|
|
96
123
|
}
|
|
97
124
|
|
|
98
|
-
const format =
|
|
125
|
+
const format =
|
|
126
|
+
type ?? (isWebp ? "webp" : contentType === "image/jpeg" ? "jpeg" : "png");
|
|
99
127
|
const image = await optimizeImage({
|
|
100
128
|
image: srcImage,
|
|
101
129
|
width: width ? parseInt(width) : undefined,
|
|
@@ -104,8 +132,8 @@ const handleRequest = async (
|
|
|
104
132
|
});
|
|
105
133
|
const response = new Response(image, {
|
|
106
134
|
headers: {
|
|
107
|
-
|
|
108
|
-
|
|
135
|
+
"Content-Type": `image/${format}`,
|
|
136
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
109
137
|
date: new Date().toUTCString(),
|
|
110
138
|
},
|
|
111
139
|
});
|
|
@@ -126,7 +154,7 @@ export default {
|
|
|
126
154
|
*/
|
|
127
155
|
const config = {
|
|
128
156
|
images: {
|
|
129
|
-
path:
|
|
157
|
+
path: "https://xxx.yyy.workers.dev/",
|
|
130
158
|
},
|
|
131
159
|
};
|
|
132
160
|
export default config;
|
|
@@ -136,16 +164,21 @@ export default config;
|
|
|
136
164
|
|
|
137
165
|
### Parameters
|
|
138
166
|
|
|
139
|
-
| Name | Type | Description
|
|
140
|
-
| ---- | ------ |
|
|
141
|
-
| url | string | Image URL
|
|
142
|
-
| q | number | Quality
|
|
143
|
-
| w | number | Width
|
|
167
|
+
| Name | Type | Description |
|
|
168
|
+
| ---- | ------ | -------------------------- |
|
|
169
|
+
| url | string | Image URL |
|
|
170
|
+
| q | number | Quality |
|
|
171
|
+
| w | number | Width |
|
|
172
|
+
| type | string | Output type(webp/png/jpeg) |
|
|
144
173
|
|
|
145
174
|
https://xxxx.deno.dev/?url=https://xxx.png&q=80&w=200
|
|
146
175
|
|
|
176
|
+
### Source code
|
|
177
|
+
|
|
178
|
+
https://github.com/SoraKumo001/deno-wasm-image-optimization
|
|
179
|
+
|
|
147
180
|
```ts
|
|
148
|
-
import { optimizeImage } from
|
|
181
|
+
import { optimizeImage } from "npm:wasm-image-optimization/esm";
|
|
149
182
|
|
|
150
183
|
const isValidUrl = (url: string) => {
|
|
151
184
|
try {
|
|
@@ -156,7 +189,7 @@ const isValidUrl = (url: string) => {
|
|
|
156
189
|
}
|
|
157
190
|
};
|
|
158
191
|
|
|
159
|
-
const cache = await caches.open(
|
|
192
|
+
const cache = await caches.open("image-cache");
|
|
160
193
|
|
|
161
194
|
Deno.serve(async (request) => {
|
|
162
195
|
const cached = await cache.match(request);
|
|
@@ -166,55 +199,59 @@ Deno.serve(async (request) => {
|
|
|
166
199
|
|
|
167
200
|
const url = new URL(request.url);
|
|
168
201
|
const params = url.searchParams;
|
|
169
|
-
const type = [
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
202
|
+
const type = ["webp", "png", "jpeg"].find((v) => v === params.get("type")) as
|
|
203
|
+
| "webp"
|
|
204
|
+
| "png"
|
|
205
|
+
| "jpeg"
|
|
173
206
|
| undefined;
|
|
174
|
-
const accept = request.headers.get(
|
|
207
|
+
const accept = request.headers.get("accept");
|
|
175
208
|
const isWebp =
|
|
176
209
|
accept
|
|
177
|
-
?.split(
|
|
210
|
+
?.split(",")
|
|
178
211
|
.map((format) => format.trim())
|
|
179
|
-
.some((format) => [
|
|
212
|
+
.some((format) => ["image/webp", "*/*", "image/*"].includes(format)) ??
|
|
213
|
+
true;
|
|
180
214
|
|
|
181
|
-
const imageUrl = params.get(
|
|
215
|
+
const imageUrl = params.get("url");
|
|
182
216
|
if (!imageUrl || !isValidUrl(imageUrl)) {
|
|
183
|
-
return new Response(
|
|
217
|
+
return new Response("url is required", { status: 400 });
|
|
184
218
|
}
|
|
185
219
|
|
|
186
|
-
url.searchParams.append(
|
|
220
|
+
url.searchParams.append("webp", isWebp.toString());
|
|
187
221
|
const cacheKey = new Request(url.toString());
|
|
188
222
|
const cachedResponse = await cache.match(cacheKey);
|
|
189
223
|
if (cachedResponse) {
|
|
190
224
|
return cachedResponse;
|
|
191
225
|
}
|
|
192
226
|
|
|
193
|
-
const width = params.get(
|
|
194
|
-
const quality = params.get(
|
|
227
|
+
const width = params.get("w");
|
|
228
|
+
const quality = params.get("q");
|
|
195
229
|
|
|
196
230
|
const [srcImage, contentType] = await fetch(imageUrl)
|
|
197
231
|
.then(async (res) =>
|
|
198
|
-
res.ok
|
|
232
|
+
res.ok
|
|
233
|
+
? ([await res.arrayBuffer(), res.headers.get("content-type")] as const)
|
|
234
|
+
: []
|
|
199
235
|
)
|
|
200
236
|
.catch(() => []);
|
|
201
237
|
|
|
202
238
|
if (!srcImage) {
|
|
203
|
-
return new Response(
|
|
239
|
+
return new Response("image not found", { status: 404 });
|
|
204
240
|
}
|
|
205
241
|
|
|
206
|
-
if (contentType && [
|
|
242
|
+
if (contentType && ["image/svg+xml", "image/gif"].includes(contentType)) {
|
|
207
243
|
const response = new Response(srcImage, {
|
|
208
244
|
headers: {
|
|
209
|
-
|
|
210
|
-
|
|
245
|
+
"Content-Type": contentType,
|
|
246
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
211
247
|
},
|
|
212
248
|
});
|
|
213
249
|
cache.put(request, response.clone());
|
|
214
250
|
return response;
|
|
215
251
|
}
|
|
216
252
|
|
|
217
|
-
const format =
|
|
253
|
+
const format =
|
|
254
|
+
type ?? (isWebp ? "webp" : contentType === "image/jpeg" ? "jpeg" : "png");
|
|
218
255
|
const image = await optimizeImage({
|
|
219
256
|
image: srcImage,
|
|
220
257
|
width: width ? parseInt(width) : undefined,
|
|
@@ -223,8 +260,8 @@ Deno.serve(async (request) => {
|
|
|
223
260
|
});
|
|
224
261
|
const response = new Response(image, {
|
|
225
262
|
headers: {
|
|
226
|
-
|
|
227
|
-
|
|
263
|
+
"Content-Type": `image/${format}`,
|
|
264
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
228
265
|
date: new Date().toUTCString(),
|
|
229
266
|
},
|
|
230
267
|
});
|