世祺 11 mesiacov pred
rodič
commit
2be75c9f01
2 zmenil súbory, kde vykonal 326 pridanie a 135 odobranie
  1. 1 0
      package.json
  2. 325 135
      src/worker.js

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "aws4fetch": "^1.0.17",
+    "file-type": "^18.5.0",
     "image-size": "^1.0.2"
   }
 }

+ 325 - 135
src/worker.js

@@ -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;
 	}
+
 };