이미지 리사이징

jathazp·2022년 3월 26일
0

0. 사전 준비

1) IAM 사용자 권한 설정

AWS Lambda 서비스를 사용하기 위해 해당 사용자에게 Lambda 권한을 주어야 한다.

사용자 -> 권한추가 -> AWSLambdaFullAccess 를 추가해야 한다.

사진 업로드도 해야 하므로 S3FullAccess 권한도 추가한다.
(나는 기존에 S3권한이 있는 사용자에게 AWSLambdaFullAccess 권한만 추가해 주었다.)

사용자에게 람다를 사용할 수 있게 권한을 부여했다면 지금부터는 우리가 올릴 lambda Function 개체에 권한을 부여해야한다.

2) IAM 역할 생성

역할 생성 ->

사용사례 : Lambda ,

권한 정책 연결 :
AWSLambdaBasicExecutionRole,
AmazonS3FullAccess
(LambdaBasicExecutionRole 은 AWS 서비스 및 리소스에 액세스 할 수 있는 권한을 부여한다.)

3) S3 버킷 정책 설정

버킷 정책에 GetObject만 주었다가 권한 에러가 났다. (CloudWatch 로그 확인)

권한 에러 발생 시 체크할 것

1) IAM 사용자 권한
2) IAM 역할 권한
3) s3 버킷 정책 Action => 난 여기서 권한 에러났다!

1. 최상위 루트에 lambda폴더 생성

S3에 업로드를 위한 폴더이므로 별도 폴더로 분리만 해주기 위한것임. 나중에 압축 후 s3에 업로드를 할텐데 전체 프로젝트 압축할 필요 없이 lambda 내용물만 압축해주면 되기 때문에 최상위 루트에서 압축함.

2. lambda 관련 라이브러리를 설치

  • npm i aws-sdk sharp (이미지 리사이징 모듈)

3. /lambda/index.js 파일 생성 및 AWS 연동

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);
    }
}

4. 우분투 ec2에 접속 후 git pull을 통해 해당 람다 파일들을 zip 압축

  • sudo apt install zip
  • /lambda$ sudo zip -r aws-upload.zip ./*
    => aws-upload.zip 생성됨

5. Linux에 AWS CLI 설치

  • sudo apt-get update
  • sudo apt-get install awscli

awscli를 설치하고나면 aws 명령어 사용이 가능해진다.
ref) https://linuxhint.com/install_aws_cli_ubuntu/

6. ec2에서 s3로 lambda 압축 파일 전송

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이 생성되면 성공

7. AWS Lambda 함수 생성

1) 함수 생성-> 함수이름만 정하고 생성해준다.

2) 생성된 함수에 아까 s3에 올린 코드를 가져와야 한다.

그 후 함수를 호출할 수 있다는 문구가 뜨면 정상

구성(Settings)에 들어가서 몇 가지 세팅을 한다.
1. 메모리 256mb 올리기 (넉넉하게)
2. 제한시간 30초로 올리기 (넉넉하게)
3. 그리고 AWS 정책 템플릿에서 새 역할 생성을 클릭 한 후 S3객체 읽기 전용 권한을 선택 (역할 이름은 아무거나 ex. s3-read)

8. Lambda > S3 트리거 생성

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을 나눠서 관리해야 하기 때문에 폴더명은 적어주는게 좋다

트리거 생성 2번째 방법

  1. 내 s3버킷으로 이동
  2. 속성 -> 이벤트 알림 생성
  3. 1) 이벤트 이름 - 아무거나
    2) 접두사 - origin/ : S3버킷 내 해당 경로에 업로드시 트리거 발생
    3) 이벤트 유형 - 모든 객체 생성 이벤트
    4) 대상 - Lambda 함수
    5) Lambda함수 지정 - Lambda 함수에서 선택
    ->아까 만든 람다 함수 지정
    이제 다시 람다 함수로 돌아가면 트리거가 생성되어 있는것을 볼 수 있다

9. S3 thumb 폴더 내에 resize 이미지 생성

lambda 함수가 실행되고 나면 S3의 thumb 폴더 내에 resize 된 이미지를 확인할 수 있다.
thumb 폴더명은 index.js 에서 직접 정해준 이름이다.

10. 기존 S3 upload 코드 수정

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 },
});
  • 업로드된 파일이 S3의 origin 폴더에 저장되도록 파일명에 origin/ 를 추가해 주었다.


//썸네일
router.post('/imagetest', upload.single('image'), (req, res) => {
    res.json({ url: req.file.location.replace('origin', 'thumb') });
});
  • req에는 origin에 저장된 폴더의 이름이 반환된다.
    thumb 이미지는 위의 경로에서 origin 만 thumb 폴더 로 바꾼 경로이므로 replace를 통해 문자열을 대체해주면 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

최종 index.js 코드

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

0개의 댓글