IT,프로그래밍/AWS

[Lambda + S3] 람다로 S3 버킷내에 파일 다른 버킷으로 옮기기!

참고자료 : http://jeonghwan-kim.github.io/node/2017/08/12/node-stream-you-need-to-know-3.html

 

개발을 하는 기능중 S3 버킷의 파일을 다른 버킷으로 이동시키는 기능을 개발 했었다.

 

기존의 소스의 경우

  1. getobject 를 해서 파일을 메모리에 buffer로 가지고 있다가
  1. putobject로 옮길 버킷에 업로드 하고.
  1. deleteObject로 기존 버킷의 파일을 삭제한다

 

get,put,delete object

static async getObject(type, fileKey) {
    try {
      const config = await utils.getEnvConfig(type);
      const result = await s3
        .getObject({ Bucket: config.bucket.origin, Key: fileKey })
        .promise()
			return result;
    } catch (error) {
      return Promise.reject(error);
    }
  }

  static async putObject(type, fileKey, fileBuffer) {
    try {
      const config = await utils.getEnvConfig(type);
      const result = await s3
        .putObject({
          Bucket: config.bucket.target,
          Key: fileKey,
          Body: fileBuffer,
        })
        .promise()
			return result;
    } catch (error) {
      return Promise.reject(error);
    }
  }

  static async deleteObject(type, fileKey) {
    try {
      const config = await utils.getEnvConfig(type);
      const result = await s3
        .deleteObject({ Bucket: config.bucket.origin, Key: fileKey })
        .promise()
      return result;
    } catch (error) {
      return Promise.reject(error);
    }
  }

 

for wait 부분

for await (const fileKey of arrFilekey) {
        const fileBuffer = await DAL.getObject(type, fileKey);
        console.log("fileBuffer  : ", fileBuffer);
        const putResult = await DAL.putObject(type, fileKey, fileBuffer);
        console.log("putBuffer  : ", putResult);
        const deleteResult = await DAL.deleteObject(type, fileKey);
        console.log("deleteResult  : ", deleteResult);
      }

 

 

여기서 문제점은 버킷에 있는 파일을 전부 메모리에 올리기 때문에 버킷에 메모리 사용량이 너무 높아진다는 문제점이 있었다.

 

이 문제를 해결하기 위해 node.js Stream을 사용해서 람다의 /tmp 폴더에 다운로드한 뒤에

stream으로 업로드하는 방식을 채택했다.

 

람다 실행시에 /tmp 는 512MiB 까지 사용할 수 있다.

 

먼저 getObejct를 스트림 방식으로 바꿔보자.

static async getObject(type, fileKey) {
    const config = await utils.getEnvConfig(type);
    return new Promise((resolve, reject) => {
      const fileStream = fs.createWriteStream("/tmp/temp", "binary");
      const s3Stream = s3
        .getObject({
          Bucket: config.bucket.origin,
          Key: fileKey,
        })
        .createReadStream();
      s3Stream
        .on("error", function (err) {
          // NoSuchKey: The specified key does not exist
          fileStream.end();
          console.error(err);
          reject(err);
        })
        .on("data", (data) => {
          console.log("data  : ", data);
        })
        .on("end", () => {
          console.log("read end");
        });
      fileStream
        .on("error", function (err) {
          console.error(err);
          fileStream.end();
          reject(err);
        })
        .on("close", function () {
          console.log("Done.");
          resolve("success");
        });
      s3Stream.pipe(fileStream);
    });
  }

 

const fileStream = fs.createWriteStream("/tmp/temp", "binary");

fs을 사용해서 /tmp/temp 라는 이름으로 writeStream로 선언해준다.

 

const s3Stream = s3
        .getObject({
          Bucket: config.bucket.origin,
          Key: fileKey,
        })
        .createReadStream();

그후 기존의 getObject에 .createReadStream()을 붙여서 stream방식으로 바꿔준다.

 

      s3Stream
        .on("error", function (err) {
          console.error(err);
          fileStream.end();
          reject(err);
        })
        .on("end", () => {
          console.log("read end");
        });
      fileStream
        .on("error", function (err) {
          console.error(err);
          fileStream.end();
          reject(err);
        })
        .on("close", function () {
          console.log("Done.");
          resolve("success");
        });

 

이후 s3Stream 과 fileStream 에 on으로 이벤트에 따라서 success처리 혹은 error처리를 명시해주고

 

s3Stream.pipe(fileStream);

 

pipe 를 사용해서 읽어 들이면서 사용할 파일을 쓰도록 만들어준다.

 

putObject도 마찬가지 방식으로 변경해준다

 

static async putObject(type, fileKey) {
    const config = await utils.getEnvConfig(type);
    console.log("Putobject");
    const fileStream = fs.createReadStream("tmp/temp");
    const s3Stream = await s3
      .putObject({
        Bucket: config.bucket.target,
        Key: fileKey,
        Body: fileStream,
      })
      .promise();
    console.log("put done : ", s3Stream);
    return s3Stream;
  }

 

이후 실행을 시켜보면 성공적으로 변경이 완료되었다!

 

다음엔 한번 다운로드를 쓰는 과정을 없애서

 

읽어 들이면서 바로 업로드 하는 방식을 시도해보려고 한다.

성공하면 또 포스팅 해보도록 하겠다