Skip to content

Instantly share code, notes, and snippets.

@sungkwangkim
Created July 30, 2022 07:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sungkwangkim/5e88b77ab48b0486c7455c52d11c2bf3 to your computer and use it in GitHub Desktop.
Save sungkwangkim/5e88b77ab48b0486c7455c52d11c2bf3 to your computer and use it in GitHub Desktop.
AWS labmda에서 squoosh를 이용하여 avif로 변환하는 코드 (에러 있음)
import axios from 'axios';
import { processBuffer } from './ImageOptimizer.mjs'
// Image types that can be handled by Sharp
// Image types that can be handled by Sharp
const AVIF = 'image/avif'
const WEBP = 'image/webp'
const PNG = 'image/png'
const JPEG = 'image/jpeg'
const TIFF = 'image/tiff'
const GIF = 'image/gif'
// ios에서 accept 해더가 */* 넘어옴.
const ANY = '*/*';
// client(browser) 캐싱 (7일)
const MAX_AGE = 60 * 60 * 24 * 7;
const SUPPORT_MIME_TYPES = [JPEG, PNG, TIFF, WEBP, AVIF];
const BYPASS_MIME_TYPES = [GIF];
const CONTENT_TYPE_TEXT_PLAIN = { 'Content-Type': 'text/plain' };
export async function handler (event, context, callback) {
// Lambda 테스트를 위해 꼭 넣어주세요. 개발인생이 편해져요.
console.log("EVENT: \n" + JSON.stringify(event, null, 2))
const { queryStringParameters, headers } = event;
const { src, w, q = 75 } = queryStringParameters;
const response = {
"statusCode": 500,
"headers": {
"Content-Type": "text/plain"
},
"isBase64Encoded": false,
"body": "noop"
};
// Extract name and format.
let href = decodeURIComponent(src);
try {
const hrefParsed = new URL(href);
href = hrefParsed.toString();
}
catch (error) {
console.log('"url" parameter is invalid');
responseUpdate(
403,
'"url" parameter is invalid', CONTENT_TYPE_TEXT_PLAIN, false
);
return callback(error);
}
// Init variables
let width;
let height;
let contentType;
let upstreamType;
let quality; // Sharp는 이미지 포맷에 따라서 품질(quality)의 기본값이 다릅니다.
let upstreamRes;
// Init sizes.
width = parseInt(w, 10) ? parseInt(w, 10) : null;
height = null;
// Init quality.
if (parseInt(q, 10)) {
quality = parseInt(q, 10);
}
// For AWS CloudWatch.
console.log(`parmas: ${JSON.stringify(queryStringParameters)}`); // Cannot convert object to primitive value.
try {
upstreamRes = await axios.get(src, {
responseType: 'arraybuffer'
}).then((res) => res);
if (upstreamRes.data.length == 0) {
responseUpdate(
404,
'The image does not exist.', CONTENT_TYPE_TEXT_PLAIN, false
);
return callback(null, response);
}
console.log("upstreamRes: \n" + JSON.stringify(upstreamRes.headers, null, 2));
// get original Format
upstreamType = upstreamRes.headers['content-type'];
}
catch (error) {
console.log('error axios: ', error);
responseUpdate(
500,
'Internal Server Error.', CONTENT_TYPE_TEXT_PLAIN, false
);
return callback(error);
}
// BYPASS return
if (BYPASS_MIME_TYPES.includes(upstreamType)) {
// 정상리턴
responseUpdate(
200,
upstreamRes.data.toString('base64'), { 'Content-Type': upstreamType, 'Cache-Control': `public, max-age=${MAX_AGE}` },
true
);
return callback(null, response);
}
// 지원하지 않는 포멧 경우 에러.
if (!SUPPORT_MIME_TYPES.some((type) => { return type == upstreamType })) {
responseUpdate(
403,
`Unsupported image type: ${upstreamType}`, CONTENT_TYPE_TEXT_PLAIN, false
);
return callback(null, response);
}
// 컨텐츠협상을 위한 값을 뽑아와요.
const accept = headers['accept'] ? headers['accept'] : "";
try {
console.log("try");
// check support for AVIF, WEBP
contentType = getSupportedMimeType(SUPPORT_MIME_TYPES, accept);
// 추후,, rotation, crop등 작업을 추가를 위해 배열로..
const operations = [];
operations.push({ type: 'resize', width })
let optimizedBuffer;
if (contentType === AVIF) {
optimizedBuffer = await processBuffer(
upstreamRes.data,
operations,
'avif',
quality
)
}
else if (contentType === WEBP) {
optimizedBuffer = await processBuffer(
upstreamRes.data,
operations,
'webp',
quality
)
}
else if (contentType === PNG) {
optimizedBuffer = await processBuffer(
upstreamRes.data,
operations,
'png',
quality
)
}
else if (contentType === JPEG) {
optimizedBuffer = await processBuffer(
upstreamRes.data,
operations,
'jpeg',
quality
)
}
console.log("optimizedBuffer: ", optimizedBuffer)
if (optimizedBuffer) {
// 최종리턴
responseUpdate(
200,
optimizedBuffer.toString('base64'), { 'Content-Type': contentType, 'Content-Disposition': 'inline', 'Cache-Control': `public, max-age=${MAX_AGE}`, 'Optimized': 'success' },
true
);
return callback(null, response);
}
else {
throw new Error('Unable to optimize buffer')
}
}
catch (error) {
console.log('error Squoosh: ', error);
responseUpdate(
500,
'Internal Server Error.', CONTENT_TYPE_TEXT_PLAIN, false
);
return callback(error);
}
function getSupportedMimeType(options = [], accept = '') {
// ios에서 accept 해더가 */* 넘어옴. iOS는 webp를 이해하므로 webp로 서빙.
if (accept === ANY) {
return WEBP;
}
else if (accept.includes(AVIF)) {
return AVIF;
}
else if (accept.includes(WEBP)) {
return WEBP;
}
else if (accept.includes(PNG)) {
return PNG;
}
else if (accept.includes(JPEG)) {
return JPEG;
}
else {
throw new Error('Unsupported encoding format')
}
}
function responseUpdate(status, body, contentHeader, isBase64Encoded = false) {
response.statusCode = status;
response.body = body;
response.headers = { ...contentHeader };
if (isBase64Encoded) {
response.isBase64Encoded = isBase64Encoded;
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment