もふもふ技術部

IT技術系mofmofメディア

serverless framework でS3バケットの作成・操作をする

serverless framework でS3バケットの作成・操作をする

概要

serverless frameworkでS3バケットを作成し、画像を登録・取得します。 リクエストにはfetchAPIを利用しました。

実装

今回の実装内容はgithubにも載せてありますのでご参考までに。 https://github.com/naok1207/serverless-framework-s3-tryal

流れ

  1. S3バケットを作成する
  2. 画像アップロード関数を作成する
  3. 画像アップロードをfetchAPIで行う
  4. 画像取得関数を作成する
  5. 画像取得をfetchAPIで行う

serverless.ymlの事前設定

provider:
  ...
  httpApi:
    cors:
      allowedOrigins:
        - "*" # 指定しないとcorsエラーとなる任意の値を設定

handler.jsの事前設定

import {
  S3Client,
  PutObjectCommand,
  GetObjectCommand,
} from "@aws-sdk/client-s3";

const client = new S3Client({});

const response = (statusCode, success, params) => {
  return {
    statusCode,
    body: JSON.stringify({
      success,
      ...params,
    }),
  };
};
export const upload = async (event, context, callback) => {}
export const getImage = async (event, context, callback) => {}

index.jsの事前設定

// lambda関数のエンドポイント
const baseUrl = "";

function onUpload() {}

function onGet() {}

1. S3バケットを作成する

providerにS3の操作権限とlambda関数とresourcesで使用する環境変数を設定

provider:
  ...
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "s3:ListBucket"
        - "s3:GetObject"
        - "s3:PutObject"
        - "s3:DeleteObject"
      Resource:
        - "*"
  environment:
    BUCKET: ${self:service}-${self:provider.stage}

resourcesにS3バケットを作成する設定を追加

resources:
  Resources:
    Bucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:provider.environment.BUCKET} # 任意の名前

2. 画像アップロード関数を作成する

handler.jsupload関数を書き換える PutObjectCommandを利用して画像の登録を行います。

export const upload = async (event, context, callback) => {
  const { base64_image, key } = JSON.parse(event.body);
  try {
    const base64 = base64_image.replace(/data:.+;base64,/, "");

    // ファイルの拡張子を取得
    const fileExtension = base64_image
      .toString()
      .slice(base64_image.indexOf("/") + 1, base64_image.indexOf(";"));

    // ContentType(image/png)を取得
    const contentType = base64_image
      .toString()
      .slice(base64_image.indexOf(":") + 1, base64_image.indexOf(";"));

    const imageBuf = Buffer.from(base64, "base64");

    const bucketParams = {
      Bucket: process.env.BUCKET,
      Key: [key, fileExtension].join("."),
      Body: imageBuf,
      ContentType: contentType,
    };

    const result = await client.send(new PutObjectCommand(bucketParams));
    console.log(result);
    return response(200, true, { message: "file saved to S3" });
  } catch (err) {
    console.error(err);
    return response(500, false, { message: "Error saving file to S3" });
  }
};

3. 画像アップロードをfetchAPIで行う

function onUpload() {
  console.log("onclick");
  const inputElement = document.getElementById("image");
  const keyElement = document.getElementById("key");
  const file = inputElement.files[0];

  const reader = new FileReader();
  reader.onload = (event) => {
    const base64 = event.currentTarget.result;
    const body = {
      base64_image: base64,
      key: keyElement.value,
    };
    fetch(baseUrl + "/upload", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    })
      .then((res) => res.json())
      .then((json) => console.log(json))
      .catch((err) => {
        console.error(err);
      });
  };
  reader.readAsDataURL(file);
}

4. 画像取得関数を作成する

export const getImage = async (event, context, callback) => {
  const { key } = event.queryStringParameters;
  try {
    const bucketParams = {
      Bucket: process.env.BUCKET,
      Key: key,
    };
    console.log({ bucketParams });
    const data = await client.send(new GetObjectCommand(bucketParams));
    const objectData = data.Body;
    const base64Data = await objectData.transformToString("base64");
    const contentType = data.ContentType;
    const body = {
      base64Data,
      contentType,
    };
    return response(200, true, {
      message: "Success to get file from S3",
      ...body,
    });
  } catch (err) {
    console.log(err);
    return response(500, false, { message: "Error get file from S3" });
  }
};

5. 画像取得をfetchAPIで行う

function onGet() {
  const keyElement = document.getElementById("show-key");
  const ImageElement = document.getElementById("show-image");
  fetch(baseUrl + "/get?key=" + keyElement.value)
    .then((res) => res.json())
    .then((json) => {
      const { base64Data, contentType } = json;
      const dataUrl = `data:${contentType};base64,${base64Data}`;
      ImageElement.src = dataUrl;
    })
    .catch((err) => console.error(err));
}

以上でlambda関数を利用してのS3への画像の登録と取得の実装が終了です。

こちらの内容にcognito authorizerを組み合わせてユーザー毎のkeyを作成したりするとまたいろいろできそうですね。

また、今回はaws-sdkではなく@aws-sdk/s3-clientを利用しました。書き方は若干異なりますが、やることはほとんど同じなので特に問題なく実装できました。

感想

作成する際にcorsに結構ハマってしまいました。serverless frameworkのApiGateway2ではoriginがデフォルトで * に設定されていると思っていましたが、そんなこともないようで、明示的にcorsの設定を行わないとダメなようでした。

会社の紹介

株式会社 mofmof では一緒に働いてくれるエンジニアを募集しています。 興味のある方は是非こちらのページよりお越しください! https://www.mof-mof.co.jp/ https://www.mof-mof.co.jp/recruit/