이미지 리사이징 서버의 구조는 이러하다.
개발팀에 공유하기 위해서 간단히 그린 그림인데 설명하자면,
이미지 요청시 캐싱된 이미지의 경우 바로 응답처리되고 캐싱되지 않은 경우, 람다함수에서 실시간 리사이징 처리되어 응답된다.
리사이징 함수의 코드는 아래와 같다.
'use strict';
const querystring = require('querystring'); // Don't install.
const AWS = require('aws-sdk'); // Don't install.
// http://sharp.pixelplumbing.com/en/stable/api-resize/
const Sharp = require('sharp');
const S3 = new AWS.S3({
signatureVersion: 'v4',
region: 'ap-northeast-2' // 버킷을 생성한 리전 입력
});
const BUCKET = '본인 s3의 버킷명' // Input your bucket
// Image types that can be handled by Sharp
const supportImageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'tiff'];
exports.handler = async(event, context, callback) => {
const { request, response } = event.Records[0].cf;
// Parameters are w, h, f, q and indicate width, height, format and quality.
const { uri } = request;
const ObjectKey = decodeURIComponent(uri).substring(1);
const params = querystring.parse(request.querystring);
const { w, h, q, f } = params
/**
* ex) https://dilgv5hokpawv.cloudfront.net/dev/thumbnail.png?w=200&h=150&f=webp&q=90
* - ObjectKey: 'dev/thumbnail.png'
* - w: '200'
* - h: '150'
* - f: 'webp'
* - q: '90'
*/
// 크기 조절이 없는 경우 원본 반환.
if (!(w || h)) {
return callback(null, response);
}
const extension = uri.match(/\/?(.*)\.(.*)/)[2].toLowerCase();
const width = parseInt(w, 10) || null;
const height = parseInt(h, 10) || null;
const quality = parseInt(q, 10) || 75; // Sharp는 이미지 포맷에 따라서 품질(quality)의 기본값이 다릅니다.
let format = (f || extension).toLowerCase();
let s3Object;
let resizedImage;
// 포맷 변환이 없는 GIF 포맷 요청은 원본 반환.
if (extension === 'gif' && !f) {
return callback(null, response);
}
// Init format.
format = format === 'jpg' ? 'jpeg' : format;
if (f && !supportImageTypes.some(type => type === f )) {//파일 포맷변환을 위한 파라미터를 요청했으나 지원하지 않는 파일 형식일 경우
responseHandler(
403,
'Forbidden',
'Unsupported image type', [{
key: 'Content-Type',
value: 'text/plain'
}],
);
return callback(null, response);
}
// Verify For AWS CloudWatch.
console.log(`parmas * : ${JSON.stringify(params)}`); // Cannot convert object to primitive value.\
console.log('S3 Object key:', ObjectKey)
try {
s3Object = await S3.getObject({
Bucket: BUCKET,
Key: ObjectKey
}).promise();
console.log('S3 Object:', s3Object);
}
catch (error) {
responseHandler(
404,
'Not Found',
'The image does not exist.', [{ key: 'Content-Type', value: 'text/plain' }],
);
return callback(null, response);
}
if (!supportImageTypes.some(type => type === extension )) {// 파일의 확장자가 지원하는 파일 형식이 아닌경우 메타데이터의 확장자로 포맷결정
format = s3Object.ContentType.split('/')[1];
}
console.log(format);
try {
resizedImage = await Sharp(s3Object.Body)
.resize(width, height)
.toFormat(format, {
quality
})
.toBuffer();
}
catch (error) {
responseHandler(
500,
'Internal Server Error',
'Fail to resize image.', [{
key: 'Content-Type',
value: 'text/plain'
}],
);
return callback(null, response);
}
// 응답 이미지 용량이 1MB 이상일 경우 원본 반환.
if (Buffer.byteLength(resizedImage, 'base64') >= 1048576) {
return callback(null, response);
}
responseHandler(
200,
'OK',
resizedImage.toString('base64'), [{
key: 'Content-Type',
value: `image/${format}`
}],
'base64'
);
/**
* @summary response 객체 수정을 위한 wrapping 함수
*/
function responseHandler(status, statusDescription, body, contentHeader, bodyEncoding) {
response.status = status;
response.statusDescription = statusDescription;
response.body = body;
response.headers['content-type'] = contentHeader;
if (bodyEncoding) {
response.bodyEncoding = bodyEncoding;
}
}
console.log('Success resizing image~~~~@@@@@');
return callback(null, response);
};
기본적인 코드는 https://devhaks.github.io/2019/08/25/aws-lambda-image-resizing/ 이 블로거를 참고했다.
이 코드에서 수정한 부분이 있는데, 확장자가 없는 파일을 호출했을때의 에러처리를 바꿨다.
확장자가 없는 이미지 파일을 올리는 경우가 많기 때문에, 이미지의 확장자명을 메타데이터의 contentType으로 구분할 수 있게 수정해서 적용했다.
'Backend > INFRA' 카테고리의 다른 글
젠킨스 도커플러그인이 인식되지 않을때 (0) | 2022.06.02 |
---|---|
Image resizing with Lambda@edge -2 (0) | 2021.11.30 |
Image resizing with Lambda@edge (0) | 2021.11.08 |