-
-
Save sungkwangkim/5e88b77ab48b0486c7455c52d11c2bf3 to your computer and use it in GitHub Desktop.
AWS labmda에서 squoosh를 이용하여 avif로 변환하는 코드 (에러 있음)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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