본문 바로가기
AWS

[Lambda] API Gateway + Lambda 를 이용하여 S3 에 이미지 업로드

by yonikim 2021. 12. 20.
728x90

aws-sdk 를 사용하여 코드 상에서 직접 업로드하는 기능을 추가하여 이미지 업로드 API 를 만들 수도 있지만,

API Gateway 와 Lambda 를 이용하여 좀더 쉽고 빠르게 업로드하는 방법을 구현해보려고 한다. 

(참고로 API Gateway 프리티어는 12개월 동안 매달 100만개씩까지만 포함되기 때문에, 그 이상이 될 경우에는 별도로 API 를 구현하는 방식을 채택하는게 좋다.)


IAM 역할 만들기

두 개의 Policy 를 붙여주자.

- AWSLambdaBasicExecutionRole: AWS CloudWatch > Log Groups 에 Lambda 로그를 남길 수 있는 권한

- AmazonS3FullAccess: AWS S3 모든 버킷에 접근할 수 있는 모든 권한 (s3:PutObject, s3:GetObject 만 있어도 되긴 한다.)

 

Lambda 함수 생성하기

"use strict";
const AWS = require("aws-sdk");
const multipart = require("parse-multipart");
const bluebird = require("bluebird");

const S3 = new AWS.S3({ region: "ap-northeast-2" });

module.exports.handler = async (event, context) => {
  console.log(JSON.stringify(event));
  const result = [];
  const bodyBuffer = Buffer.from(event["body-json"].toString(), "base64");

  let { header } = event.params;
  header = Object.keys(header).reduce(
    (c, k) => ((c[k.toLowerCase()] = header[k]), c),
    {},
  );
  const boundary = multipart.getBoundary(header["content-type"]);

  const parts = multipart.Parse(bodyBuffer, boundary);

  const files = getFiles(parts);

  return bluebird
    .map(files, ({ params, uploadFile }) => {
      return upload({ params, uploadFile }).then(
        (data) => {
          result.push({ data, fileUrl: uploadFile.fullPath });
        },
        (err) => {
          console.log(` !!!!!!!!! Occurred Error When Upload S3 =====> ${err}`);
        },
      );
    })
    .then((_) => {
      return context.succeed(result);
    });
};

const upload = ({ params, uploadFile }) => {
  return S3.upload(params).promise();
};

const getFiles = (parts) => {
  const files = [];
  parts.forEach((part) => {
    const buffer = part.data;
    const fileFullName = decodeURIComponent(escape(part.filename));
    const fileFullPath = `https://${BUCKET_NAME}/${fileFullName}`;

    const params = {
      Bucket: BUCKET_NAME,
      Key: `${fileFullName}`,
      Body: buffer,
    };
    const uploadFile = {
      size: buffer.toString("ascii").length,
      type: part.type,
      name: fileFullName,
      fullPath: fileFullPath,
    };

    files.push({ params, uploadFile });
  });
  return files;
};

 

※ part.filename 의 한글이 계속 깨져서 몇시간 동안 애먹었었는데, JAVA 의 경우에는 아래 코드로 해결할 수 있고

new String(filename.getBytes("ISO-8859-1"), "UTF-8")

Node JS 의 경우에는 아래 코드로 해결할 수 있었다.

decodeURIComponent(escape(filename))

 

 

API Gateway 만들기

AWS API Gateway > [Create API] > REST API

 

리스소 생성

 

메서드 생성

 

이진 미디어 형식 추가

이진 미디어를 추가해주는 이유는 multipart/form-part 형식으로 보낸 파일들을 API Gateway 에서 이진 바이너리 형식으로 변환하여 처리해주기 때문이다. 

 

 

AWS S3 정책 추가하기

[권한] 탭 > 퍼블릭 액세스 차단을 비활성화 처리

 

버킷 정책 편집

{
    "Version": "2012-10-17",
    "Id": "Policy1639632675212",
    "Statement": [
        {
            "Sid": "Stmt1639632673739",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": `${S3 Bucket ARN}/*`
        }
    ]
}

 

퍼블릭 액세스 차단 다시 활성화 처리

 

API Gateway 설정 마무리

 

 

배포

 


 

테스트

포스트맨으로 테스트를 해봐도 되고, 나같은 경우에는 코드펜(https://codepen.io/pen/) 에서 html 코드를 작성하여 테스트해봤다.

<form method="post" action=`${위에서 복사한 API Gateway URL}/image` enctype="multipart/form-data">
  <div>
     파일들: <input type="file" name="files" multiple>
  </div>
  <input type="submit">
</form>

 

728x90