|
@@ -1,148 +1,338 @@
|
|
|
-const default_host = 'litb-live-image.s3.amazonaws.com';
|
|
|
const excludePath = /.*\/.*desc_images\/.*/;
|
|
|
const sizeOf = require('image-size');
|
|
|
+const imageSign = {
|
|
|
+ 'bmp': {
|
|
|
+ 'signs': [
|
|
|
+ '0,424D'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/bmp'
|
|
|
+ },
|
|
|
+ 'dib': {
|
|
|
+ 'signs': [
|
|
|
+ '0,424D'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/bmp'
|
|
|
+ },
|
|
|
+ 'emf': {
|
|
|
+ 'signs': [
|
|
|
+ '0,01000000'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/emf'
|
|
|
+ },
|
|
|
+ 'fits': {
|
|
|
+ 'signs': [
|
|
|
+ '0,53494D504C4520203D202020202020202020202020202020202020202054'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/fits'
|
|
|
+ },
|
|
|
+ 'gif': {
|
|
|
+ 'signs': [
|
|
|
+ '0,474946383961'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/gif'
|
|
|
+ },
|
|
|
+ 'jp2': {
|
|
|
+ 'signs': [
|
|
|
+ '0,0000000C6A5020200D0A'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/jp2'
|
|
|
+ },
|
|
|
+ 'jpg': {
|
|
|
+ 'signs': [
|
|
|
+ '0,FFD8'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/jpeg'
|
|
|
+ },
|
|
|
+ 'png': {
|
|
|
+ 'signs': [
|
|
|
+ '0,89504E470D0A1A0A'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/png'
|
|
|
+ },
|
|
|
+ 'tiff': {
|
|
|
+ 'signs': [
|
|
|
+ '0,492049',
|
|
|
+ '0,49492A00',
|
|
|
+ '0,4D4D002A',
|
|
|
+ '0,4D4D002B'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/tiff'
|
|
|
+ },
|
|
|
+ 'psd': {
|
|
|
+ 'signs': [
|
|
|
+ '0,38425053'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/vnd.adobe.photoshop'
|
|
|
+ },
|
|
|
+ 'dwg': {
|
|
|
+ 'signs': [
|
|
|
+ '0,41433130'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/vnd.dwg'
|
|
|
+ },
|
|
|
+ 'ico': {
|
|
|
+ 'signs': [
|
|
|
+ '0,00000100'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/vnd.microsoft.icon'
|
|
|
+ },
|
|
|
+ 'mdi': {
|
|
|
+ 'signs': [
|
|
|
+ '0,4550'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/vnd.ms-modi'
|
|
|
+ },
|
|
|
+ 'hdr': {
|
|
|
+ 'signs': [
|
|
|
+ '0,233F52414449414E43450A',
|
|
|
+ '0,49536328'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/vnd.radiance'
|
|
|
+ },
|
|
|
+ // "pcx": {
|
|
|
+ // "signs": [
|
|
|
+ // "512,0908100000060500"
|
|
|
+ // ],
|
|
|
+ // "mime": "image/vnd.zbrush.pcx"
|
|
|
+ // },
|
|
|
+ 'wmf': {
|
|
|
+ 'signs': [
|
|
|
+ '0,010009000003',
|
|
|
+ '0,D7CDC69A'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/wmf'
|
|
|
+ },
|
|
|
+ 'webp': {
|
|
|
+ 'signs': [
|
|
|
+ '0,52494646'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/webp'
|
|
|
+ },
|
|
|
+ 'pgm': {
|
|
|
+ 'signs': [
|
|
|
+ '0,50350A'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/x-portable-graymap'
|
|
|
+ },
|
|
|
+ 'rgb': {
|
|
|
+ 'signs': [
|
|
|
+ '0,01DA01010003'
|
|
|
+ ],
|
|
|
+ 'mime': 'image/x-rgb'
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+const CONTENT_TYPE = 'Content-Type';
|
|
|
+const OCTET_STREAM = 'application/octet-stream';
|
|
|
+
|
|
|
+function guessMIME(url) {
|
|
|
+ let tail = url.pathname;
|
|
|
+ let index = tail.lastIndexOf('.') + 1;
|
|
|
+ let imageSignElement = imageSign[tail.slice(index)];
|
|
|
+ return imageSignElement ? imageSignElement.mime : OCTET_STREAM;
|
|
|
+}
|
|
|
+
|
|
|
+// function getMimeTypeFromArrayBuffer(arrayBuffer) {
|
|
|
+
|
|
|
+
|
|
|
+// const uint8arr = new Uint8Array(arrayBuffer);
|
|
|
+// const len = 4;
|
|
|
+// if (uint8arr.length >= len) {
|
|
|
+// let signatureArr = new Array(len);
|
|
|
+// for (let i = 0; i < len; i++)
|
|
|
+// signatureArr[i] = (new Uint8Array(arrayBuffer))[i].toString(16);
|
|
|
+// const signature = signatureArr.join('').toUpperCase();
|
|
|
+// for (let t in imageSign) {
|
|
|
+// if (imageSign[t].signs.some(sign => sign.slice(2).startsWith(signature))) {
|
|
|
+// return imageSign[t].mime;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// return 'application/octet-stream';
|
|
|
+// }
|
|
|
+function buildUrl(width, height, tail, host) {
|
|
|
+
|
|
|
+ if (Number(width) >= 500 || Number(height) >= 500) {
|
|
|
+ let index = tail.lastIndexOf('.');
|
|
|
+ tail = tail.slice(0, index) + '_1500' + tail.slice(index);
|
|
|
+ }
|
|
|
+ return new URL(tail, host);
|
|
|
+}
|
|
|
+
|
|
|
+const host = ['https://litb-live-image.s3.amazonaws.com', 'https://d1453ta0frklga.cloudfront.net', 'https://dlvvnch56i9xa.cloudfront.net'];
|
|
|
+
|
|
|
+const rules = [{
|
|
|
+ reg: /\/images\/(\d+)x(\d+)(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname) && !excludePath.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [_, width, height, tail] = url.pathname.match(this.reg);
|
|
|
+ return {
|
|
|
+ image: { width: Number(width), height: Number(height) },
|
|
|
+ dstUrl: buildUrl(width, height, tail, host[1])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/images\/x(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname) && !excludePath.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [, tail] = url.pathname.match(this.reg);
|
|
|
+ return {
|
|
|
+ image: { width: 1140, height: 1500 },
|
|
|
+ dstUrl: buildUrl(1140, 1500, tail, host[1])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ regs: [/\/images\/middle(\/.*)$/, /\/images\/small(\/.*)$/, /\/images\/l(\/.*)$/, /\/images\/f(\/.*)$/, /\/images\/m(\/.*)$/, /\/images\/p(\/.*)$/, /\/images\/s(\/.*)$/],
|
|
|
+ test: function(url) {
|
|
|
+ return this.regs.some(reg => reg.test(url.pathname)) && !excludePath.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let regExp = this.regs.find(reg => reg.test(url.pathname));
|
|
|
+ let [, tail] = url.pathname.match(regExp);
|
|
|
+ return {
|
|
|
+ image: { width: 280, height: 280 },
|
|
|
+ dstUrl: buildUrl(280, 280, tail, host[1])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/images\/v(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname) && !excludePath.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [, tail] = url.pathname.match(this.reg);
|
|
|
+ return {
|
|
|
+ image: { width: 500, height: 500 },
|
|
|
+ dstUrl: buildUrl(500, 500, tail, host[1])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/images\/\?(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname + url.search) && !excludePath.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [, tail] = (url.pathname + url.search).match(this.reg);
|
|
|
+ return {
|
|
|
+ image: { width: 380, height: 500 },
|
|
|
+ dstUrl: buildUrl(380, 500, tail, host[1])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/images(_litb)?(_mini|_ouku)?\/review(_new)?\/(\d+)x(\d+)(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [, , m2, , width, height, tail] = url.pathname.match(this.reg);
|
|
|
+ return {
|
|
|
+ image: { width: Number(width), height: Number(height) },
|
|
|
+ dstUrl: buildUrl(width, height, `/images${m2 || ''}/review/mobile${tail}`, host[2])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/images\/(dfp|brand|wholesale)(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [tail] = url.pathname.match(this.reg);
|
|
|
+ return {
|
|
|
+ image: {},
|
|
|
+ dstUrl: buildUrl(0, 0, tail, host[2])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/images\/cust_img_bg(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [, tail] = url.pathname.match(this.reg);
|
|
|
+ return {
|
|
|
+ image: {},
|
|
|
+ dstUrl: buildUrl(0, 0, '/cust_img_bg' + tail, host[0])
|
|
|
+ };
|
|
|
+ }
|
|
|
+}, {
|
|
|
+ reg: /\/(webp_)?desc_image(\/.*)$/,
|
|
|
+ test: function(url) {
|
|
|
+ return this.reg.test(url.pathname);
|
|
|
+ },
|
|
|
+ parse: async function(url) {
|
|
|
+ let [, , tail] = url.pathname.match(this.reg);
|
|
|
+
|
|
|
+ let dstUrl = buildUrl(0, 0, '/desc_image' + tail, host[2]);
|
|
|
+ let response = await fetch(dstUrl);
|
|
|
+ let buffer = await response.arrayBuffer();
|
|
|
+ let { width, height } = sizeOf(Buffer.from(buffer));
|
|
|
+ let image = width > 6000 || height > 6000 ? {
|
|
|
+ height: 1500, width: 1500
|
|
|
+ } : {};
|
|
|
+
|
|
|
+ return {
|
|
|
+ image,
|
|
|
+ dstUrl,
|
|
|
+ buffer
|
|
|
+ };
|
|
|
+ }
|
|
|
+}];
|
|
|
+
|
|
|
export default {
|
|
|
async fetch(request, _env, _ctx) {
|
|
|
-
|
|
|
- if (/image-resizing/.test(request.headers.get('Via'))) {
|
|
|
+ if (/image-resizing/.test(request.headers.get('Via')))
|
|
|
return fetch(request);
|
|
|
+ let originUrl = new URL(request.url);
|
|
|
+ let rule = rules.find(rule => rule.test(originUrl));
|
|
|
+ let dstMIME = guessMIME(originUrl);
|
|
|
+
|
|
|
+ //无规则匹配,直接检查content-type
|
|
|
+ if (!rule) {
|
|
|
+ let originResponse = await fetch(originUrl);
|
|
|
+ originResponse = new Response(originResponse.body, { headers: new Headers(originResponse.headers) });
|
|
|
+ console.log(CONTENT_TYPE, dstMIME);
|
|
|
+ if (originResponse.headers.get(CONTENT_TYPE) === OCTET_STREAM)
|
|
|
+ originResponse.headers.set(CONTENT_TYPE, dstMIME);
|
|
|
+ return originResponse;
|
|
|
}
|
|
|
- let image = {};
|
|
|
- let url = new URL(request.url);
|
|
|
-
|
|
|
- let path = url.pathname;
|
|
|
-
|
|
|
- let newUrl = new URL(request.url);
|
|
|
-
|
|
|
- let reg3 = [/\/images\/middle(\/.*)$/, /\/images\/small(\/.*)$/, /\/images\/l(\/.*)$/, /\/images\/f(\/.*)$/, /\/images\/m(\/.*)$/, /\/images\/p(\/.*)$/, /\/images\/s(\/.*)$/];
|
|
|
-
|
|
|
- let func = (reg, width, height, tp, wp = null, hp = null) => {
|
|
|
- newUrl.host = 'd1453ta0frklga.cloudfront.net';
|
|
|
- let match = path.match(reg);
|
|
|
-
|
|
|
- if (wp) width = Number(match[wp]);
|
|
|
- if (hp) height = Number(match[hp]);
|
|
|
- let suffix = (width >= 500 || height >= 500) ? '_1500' : '';
|
|
|
- let tail = match[tp];
|
|
|
- image = {
|
|
|
- height,
|
|
|
- width,
|
|
|
- fit: 'pad'
|
|
|
- };
|
|
|
- let index = tail.lastIndexOf('.');
|
|
|
- newUrl.pathname = tail.slice(0, index) + suffix + tail.slice(index);
|
|
|
- };
|
|
|
|
|
|
+ //依据规则设置处理参数与回源链接
|
|
|
+ let { image, dstUrl, buffer } = await rule.parse(originUrl);
|
|
|
|
|
|
- if (/\/images\/(\d+)x(\d+)(\/.*)$/.test(path) && (!excludePath.test(path))) {
|
|
|
-
|
|
|
- func(/\/images\/(\d+)x(\d+)(\/.*)$/, null, null, 3, 1, 2);
|
|
|
-
|
|
|
- } else if (/\/images\/x(\/.*)$/.test(path) && (!excludePath.test(path))) {
|
|
|
-
|
|
|
- func(/\/images\/x(\/.*)$/, 1140, 1500, 1);
|
|
|
-
|
|
|
- } else if (reg3.some(reg => reg.test(path)) && (!excludePath.test(path))) {
|
|
|
-
|
|
|
- let regExp = reg3.find(reg => reg.test(path));
|
|
|
- func(regExp, 280, 280, 1);
|
|
|
-
|
|
|
- } else if (/\/images\/v(\/.*)$/.test(path) && (!excludePath.test(path))) {
|
|
|
-
|
|
|
- func(/\/images\/v(\/.*)$/, 500, 500, 1);
|
|
|
-
|
|
|
- } else if (/\/images\/\?(\/.*)$/.test(url.pathname + url.search) && (!excludePath.test(url.pathname + url.search))) {
|
|
|
- let match = (url.pathname + url.search).match(/\/images\/\?(\/.*)$/);
|
|
|
-
|
|
|
- let height = 500;
|
|
|
- let width = 380;
|
|
|
- let suffix = (width >= 500 || height >= 500) ? '_1500' : '';
|
|
|
- let tail = match[1];
|
|
|
-
|
|
|
- image = {
|
|
|
- height,
|
|
|
- width,
|
|
|
- fit: 'pad'
|
|
|
- };
|
|
|
-
|
|
|
- let index = tail.lastIndexOf('.');
|
|
|
-
|
|
|
- newUrl.host = 'd1453ta0frklga.cloudfront.net';
|
|
|
- newUrl.pathname = tail.slice(0, index) + suffix + tail.slice(index);
|
|
|
- newUrl.search = '';
|
|
|
- } else if (/\/images(_litb)?(_mini|_ouku)?\/review(_new)?\/(\d+)x(\d+)(\/.*)$/.test(path)) {
|
|
|
- let match = path.match(/\/images(_litb)?(_mini|_ouku)?\/review(_new)?\/(\d+)x(\d+)(\/.*)$/);
|
|
|
-
|
|
|
- let width = Number(match[4]);
|
|
|
- let height = Number(match[5]);
|
|
|
- let suffix = (width >= 500 || height >= 500) ? '_1500' : '';
|
|
|
- let tail = match[6];
|
|
|
- image = {
|
|
|
- height,
|
|
|
- width,
|
|
|
- fit: 'pad'
|
|
|
- };
|
|
|
- let index = tail.lastIndexOf('.');
|
|
|
- newUrl.pathname = '/images' + (match[2] ? match[2] : '') + '/review/mobile' + tail.slice(0, index) + suffix + tail.slice(index);
|
|
|
- newUrl.host = 'dlvvnch56i9xa.cloudfront.net';
|
|
|
-
|
|
|
- } else if (/\/images\/(dfp|brand|wholesale)(\/.*)$/.test(path)) {
|
|
|
- let match = url.pathname.match(/\/images\/(dfp|brand|wholesale)(\/.*)$/);
|
|
|
- newUrl.host = 'dlvvnch56i9xa.cloudfront.net';
|
|
|
- newUrl.pathname = '/images/' + match[1] + match[2];
|
|
|
-
|
|
|
- } else if (/\/images\/cust_img_bg(\/.*)$/.test(path)) {
|
|
|
- let match = url.pathname.match(/\/images\/cust_img_bg(\/.*)$/);
|
|
|
- newUrl.host = url.host;
|
|
|
- newUrl.pathname = '/cust_img_bg' + match[1];
|
|
|
- } else if (/\/(webp_)?desc_image(\/.*)$/.test(path)) {
|
|
|
- let match = url.pathname.match(/\/(webp_)?desc_image(\/.*)$/);
|
|
|
- newUrl.host = 'dlvvnch56i9xa.cloudfront.net';
|
|
|
- newUrl.pathname = '/desc_image' + match[2];
|
|
|
- console.log(newUrl);
|
|
|
- let response = await fetch(newUrl);
|
|
|
-
|
|
|
- let buffer = await response.arrayBuffer();
|
|
|
- let size = sizeOf(Buffer.from(buffer));
|
|
|
- let largeSize = size.width > 6000 || size.height > 6000;
|
|
|
- let requireWebp = url.searchParams.get('fmt') === 'webp';
|
|
|
- console.log(size.width,"x",size.height);
|
|
|
- console.log(requireWebp);
|
|
|
- if (largeSize && requireWebp) {
|
|
|
- return await fetch(newUrl, {
|
|
|
- cf: {
|
|
|
- image: {
|
|
|
- height: 1500,
|
|
|
- width: 1500,
|
|
|
- fit: 'pad',
|
|
|
- format: 'webp'
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- } else if (largeSize && !requireWebp) {
|
|
|
- return await fetch(newUrl, {
|
|
|
- cf: {
|
|
|
- image: {
|
|
|
- height: 1500,
|
|
|
- width: 1500,
|
|
|
- fit: 'pad'
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- } else if (!largeSize && requireWebp) {
|
|
|
- return await fetch(newUrl, {
|
|
|
- cf: {
|
|
|
- image: {
|
|
|
- format: 'webp'
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- } else return await fetch(newUrl);
|
|
|
+ //检查源长宽比与目标长宽比,确定图像适应模式
|
|
|
+ if (!buffer) {
|
|
|
+ let response = await fetch(dstUrl);
|
|
|
+ buffer = await response.arrayBuffer();
|
|
|
}
|
|
|
- console.log(newUrl);
|
|
|
- console.log(image);
|
|
|
- return await fetch(newUrl, {
|
|
|
+ let { width, height } = sizeOf(Buffer.from(buffer));
|
|
|
+ if (image.width && image.height)
|
|
|
+ image.fit = image.width / image.height > width / height ? 'pad' : 'cover';
|
|
|
+
|
|
|
+ //检查是否需要转换为webp格式
|
|
|
+ if (originUrl.searchParams.get('fmt') === 'webp')
|
|
|
+ image.format = 'webp';
|
|
|
+
|
|
|
+
|
|
|
+ let response = await fetch(dstUrl, {
|
|
|
cf: { image }
|
|
|
});
|
|
|
+ //检查返回结果的content-type格式
|
|
|
+ let content = response.headers.get(CONTENT_TYPE);
|
|
|
+ if (content === OCTET_STREAM) {
|
|
|
+ response = new Response(response.body, { headers: new Headers(response.headers) });
|
|
|
+ if (image.format)
|
|
|
+ dstMIME = 'image/png';
|
|
|
+ response.headers.set(CONTENT_TYPE, dstMIME);
|
|
|
+ console.log(`set content type ${dstMIME}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ //返回最终响应
|
|
|
+ return response;
|
|
|
}
|
|
|
+
|
|
|
};
|