AWS Lambda 서비스를 사용하기 위해 해당 사용자에게 Lambda 권한을 주어야 한다.
사용자 -> 권한추가 -> AWSLambdaFullAccess 를 추가해야 한다.
사진 업로드도 해야 하므로 S3FullAccess 권한도 추가한다.
(나는 기존에 S3권한이 있는 사용자에게 AWSLambdaFullAccess 권한만 추가해 주었다.)
사용자에게 람다를 사용할 수 있게 권한을 부여했다면 지금부터는 우리가 올릴 lambda Function 개체에 권한을 부여해야한다.
역할 생성 ->
사용사례 : Lambda ,
권한 정책 연결 :
AWSLambdaBasicExecutionRole,
AmazonS3FullAccess
(LambdaBasicExecutionRole 은 AWS 서비스 및 리소스에 액세스 할 수 있는 권한을 부여한다.)
버킷 정책에 GetObject만 주었다가 권한 에러가 났다. (CloudWatch 로그 확인)
권한 에러 발생 시 체크할 것
1) IAM 사용자 권한
2) IAM 역할 권한
3) s3 버킷 정책 Action => 난 여기서 권한 에러났다!
S3에 업로드를 위한 폴더이므로 별도 폴더로 분리만 해주기 위한것임. 나중에 압축 후 s3에 업로드를 할텐데 전체 프로젝트 압축할 필요 없이 lambda 내용물만 압축해주면 되기 때문에 최상위 루트에서 압축함.
const AWS = require('aws-sdk');
const sharp = require('sharp');
const s3 = new AWS.S3();
exports.handler = async (event, context, callback) => {
const Bucket = event.Records[0].s3.bucket.name; // react-nodebird-loosie
const Key = decodeURIComponent(event.Records[0].s3.object.key); // original/123123123_abc.png
console.log(Bucket, Key);
const filename = Key.split('/')[Key.split('/').length - 1];
const ext = Key.split('.')[Key.split('.').length - 1].toLowerCase();
const requiredFormat = ext === 'jpg' ? 'jpeg' : ext;
console.log('filename : ', filename, 'ext', ext);
try{
const s3Object = await s3.getObject({ Bucket, Key }).promise();
console.log('original', s3Object.Body.length);
const resizedImage = await sharp(s3Object.Body)
.resize(400, 400, { fit: 'inside'})
.toFormat(requiredFormat)
.toBuffer();
await s3.putObject({
Bucket,
Key: `thumb/${filename}`,
Body: resizedImage,
}).promise();
console.log('put', resizedImage.length);
return callback(null, `thumb/${filename}`);
} catch (error){
console.error(error);
return callback(error);
}
}
awscli를 설치하고나면 aws 명령어 사용이 가능해진다.
ref) https://linuxhint.com/install_aws_cli_ubuntu/
s3에 업로드 하기 위해 먼저 aws 계정 등록을 해주어야 한다.
$ aws configure
$ AWS Access Key ID [None] : 자신의 S3 Access 키 값 입력
$ AWS Secret Access Key [None]: 자신의 S3 Secret 키 값 입력
$ Default region name [None]: 자신의 aws 지역 입력 (ex. ap-northeast-2)
$ Default output format [None]: json
계정 등록을 완료했으면 cli를 통해 s3로 업로드가 가능하다
aws s3 cp "aws-upload.zip" s3://[S3 버컷 이름]
ex) aws s3 cp "aws-upload.zip" s3://hanghae99-velogclone
s3에 aws-upload.zip이 생성되면 성공
1) 함수 생성-> 함수이름만 정하고 생성해준다.
2) 생성된 함수에 아까 s3에 올린 코드를 가져와야 한다.
그 후 함수를 호출할 수 있다는 문구가 뜨면 정상
구성(Settings)에 들어가서 몇 가지 세팅을 한다.
1. 메모리 256mb 올리기 (넉넉하게)
2. 제한시간 30초로 올리기 (넉넉하게)
3. 그리고 AWS 정책 템플릿에서 새 역할 생성을 클릭 한 후 S3객체 읽기 전용 권한을 선택 (역할 이름은 아무거나 ex. s3-read)
S3에 업로드 될때 Lambda 함수가 실행되도록 트리거를 걸어야 한다.
1) 버킷은 자신의 S3 버킷 선택
2) 모든 객체 생성 이벤트 (origin image를 이용해 resize이미지를 생성하므로 s3:ObjectCreatedPut 이벤트를 선택해도 무관할듯하다)
3) 접두사 (필수 입력!)
S3 이미지가 저장되는 폴더이름을 입력하면 된다.
S3 내에 폴더를 하나 생성하고 ex) origin/
해당 폴더의 이름을 넣어주면 된다.
그러면 origin 폴더에 들어갈때 트리거가 발생해 람다 함수 코드를 실행하게 된다.
=> 그냥 / 로 입력하면 어떻게 될까 생각해봤는데
S3에 업로드가 되는 순간 트리거가 걸리고, index.js 코드 내용에 따라 S3의 thumb 폴더에 resize된 이미지가 업로드 될텐데,
이 thumb폴더의 내용물도 결국 S3에 업로드 되는것이므로 다시 트리거가 걸려서 resize 이미지가 또다시 s3에 업로드가 되고..
재귀적으로 업로드가 될 수도 있지않을까.. 하는 생각이 들었다. (트리거 생성 전, 재귀 호출에 관한 경고문구가 있어서 이런 생각이 들었다..)
직접 해보지는 않았지만 어차피 thumb 과 origin을 나눠서 관리해야 하기 때문에 폴더명은 적어주는게 좋다
lambda 함수가 실행되고 나면 S3의 thumb 폴더 내에 resize 된 이미지를 확인할 수 있다.
thumb 폴더명은 index.js 에서 직접 정해준 이름이다.
const upload = multer({
storage: multerS3({
s3: s3,
bucket: S3_BUCKET_NAME,
contentType: multerS3.AUTO_CONTENT_TYPE,
acl: 'public-read',
key: function (req, file, cb) {
cb(
null,
'origin/' + Math.floor(Math.random() * 1000).toString() +
Date.now() +
'.' +
file.originalname.split('.').pop()
); // 이름 설정
},
}),
limits: { fileSize: 1024 * 1024 * 10 },
});
//썸네일
router.post('/imagetest', upload.single('image'), (req, res) => {
res.json({ url: req.file.location.replace('origin', 'thumb') });
});
이미지 리사이징시 회전하는 현상
:리사이징 과정에서의 meta 데이터 유실로 인한 버그
exif데이터를 포함할 수 있도록 withMetadata()를 추가
주의) toFormat('png')의 경우 jpeg와 동일한 exif를 지원하지 않으므로 빼야한다.
S3.getObject({Bucket: BUCKET, Key: originalKey}).promise()
.then(data => Sharp(data.Body)
.resize(width, height)
.withMetadata() // add this line here
.toBuffer()
)
참고)
이미지 리사이징시 이미지 회전 현상
이미지 리사이징시 이미지 회전 현상 stackoverflow
const AWS = require('aws-sdk');
const sharp = require('sharp');
const s3 = new AWS.S3();
exports.handler = async (event, context, callback) => {
const Bucket = event.Records[0].s3.bucket.name; // react-nodebird-loosie
const Key = decodeURIComponent(event.Records[0].s3.object.key); // original/123123123_abc.png
console.log(Bucket, Key);
const filename = Key.split('/')[Key.split('/').length - 1];
try {
const s3Object = await s3.getObject({ Bucket, Key }).promise();
console.log('original', s3Object.Body.length);
const resizedImage = await sharp(s3Object.Body)
.resize(384, 384, { fit: 'inside' })
.toFormat('jpeg')
.withMetadata()
.toBuffer();
await s3
.putObject({
Bucket,
Key: `thumb/${filename}`,
Body: resizedImage,
})
.promise();
console.log('put', resizedImage.length);
return callback(null, `thumb/${filename}`);
} catch (error) {
console.error(error);
return callback(error);
}
};
ref)
https://linuxhint.com/install_aws_cli_ubuntu/
***https://loosie.tistory.com/138
https://mingyucloud.tistory.com/entry/AWS-Lambda-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%83%9D%EC%84%B1