LoginSignup
4
2

More than 5 years have passed since last update.

[AWS×ServerlessFramework]AmazonAPIGateway経由でS3にJSONを保存する

Last updated at Posted at 2018-04-04

はじめに

みなさん。IoTやっていて、こんなこと思ったことありませんか?

「Lambdaでデータを整形して、DynamoDBに保存するなんて大層なことせんでもいいから、デバイスから送ったテキストデータを任意の名前でS3に保存できるだけで十分なのにな〜」

私はあります。(超個人的意見)
なんでそんなこと思ったのか?と言いますと、お客様からこんな要望が上がって来ました。

「デバイスとAWSの疎通をとりあえず、なる早で確認したい。ついでにテキストデータをどこかに保存して欲しい。」

こんなときにサクッとできないかな〜と思って、ServerlessFramework使って実装しました。
別にIoT以外でも利用できる場面はあると思います。
主な用途としては、 APIGatewayにファイル(JSONなどのテキストファイル)をPOSTしてS3にPUTする となります。

アーキテクチャ

スクリーンショット 2018-04-04 13.33.16.png

フローとしては
1. Deviceから APIGatewayのEP/send/{fileName} にPOSTする
2. APIGatewayからLambdaをキックする
3. LambdaからPOSTのBodyをS3にkeyを{fileName}としてPUTする
みたいな感じです。

ソースコード

ServerlessFrameworkのインストールやプロジェクトの作成はこの辺りの記事を参考にしてください。
参考記事はこちら

serverless.yml はこんな感じ

serverless.yml

service: apigateway-sample

# 基本設定
custom:
  defaultStage: dev
  defaultRegion: ap-northeast-1

# LambdaのruntimeやiamRoleなどの設定
# APIKeyを用意して、APIGatewayに適用します
# iamRole
#   S3 BucketName:serverless-apigateway-sample-bucketに対してGetObject/PutObjectを許可
provider:
  name: aws
  runtime: nodejs6.10
  stage: ${opt:stage, self:custom.defaultStage}
  region: ${opt:region, self:custom.defaultRegion}
  apiKeys:
    - ${self:custom.defaultStage}-send-apiKey
  usegePlan:
    quota:
      limit: 5000
      offset: 2
      period: MONTH
    throttle:
      burstLimit: 200
      rateLimit: 100
  iamRoleStatements:
    - Effect: "Allow"
      Resource: "arn:aws:s3:::serverless-apigateway-sample-bucket"
      Action:
        - "s3:GetObject"
        - "s3:PutObject"

# Lambdaのhandlerやevent(トリガー)の設定
# APIGatewayにAPIKeyを適用しないなら、private: falseに変更
functions:
  putS3FromApiGateway:
    handler: index.handler
    description: iot-serverless-sample LambdaFunction from APIGateway
    events:
      - http:
          path: send/{fileName}
          method: post
          cors: true
          private: true

# その他各種サービスリソースの設定
# 今回はS3のBucketを作成
# BucketNameは適宜書き換えてください
resources:
  Resources:
    S3Bucket:
      Type: "AWS::S3::Bucket"
      Properties:
        BucketName: BucketName
    BucketPolicy:
      Type: "AWS::S3::BucketPolicy"
      Properties:
        PolicyDocument:
          Id: "serverless-sample-bucket-policy"
          Version: "2012-10-17"
          Statement:
            - Sid: "AllowPutObjects"
              Effect: "Allow"
              Principal: "*"
              Action:
                - "s3:GetObject"
                - "s3:PutObject"
              Resource: "arn:aws:s3:::serverless-apigateway-sample-bucket/*"
        Bucket: "serverless-apigateway-sample-bucket"

Lambdaのソースコードはこんな感じ

index.js
"use strict";

const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const config = require("../config");

exports.handler = (event, context, callback) => {
  // POSTリクエストのBody(JSON)を取得
  const reqBody = JSON.parse(event["body"]);
  // S3にPUTする際のKey名(ファイル名)を取得
  const fileName = event["pathParameters"]["fileName"];
  // S3にPUTするパラメータを形成
  // serverless.ymlで指定したBucketNameに書き換えてください
  const param = {
    Bucket: BucketName,
    Key: fileName,
    Body: JSON.stringify(reqBody)
  };
  // S3にPutObject
  return s3.putObject(param).promise()
    .then(() => {
      console.log("All Done");
      callback(null, {
        statusCode: 200,
        headers:{ "Access-Control-Allow-Origin" : "*" },
        body: JSON.stringify({message: "SUCCEEDED!", res: ""})
      });
    }).catch((err) => {
      console.error(err);
      callback(null, {
        statusCode: 400,
        headers:{ "Access-Control-Allow-Origin" : "*" },
        body: JSON.stringify({message: "ERROR!", error: err})
      });
    });
};

Deploy実行

作成した serverless.yml があるディレクトリで下記コマンドを実行します。

$ sls deploy
・
・
・
・
Service Information
service: apigateway-sample
stage: dev
region: ap-northeast-1
stack: apigateway-sample-dev
api keys:
  dev-send-apiKey: APIKey  //APIKeyの文字列が表示されます
endpoints:
  POST - https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/send/{fileName}
functions:
  putS3FromApiGateway: apigateway-sample-dev-putS3FromApiGateway

成功すれば上記のようなメッセージが表示され、APIGatewayのエンドポイントやAPIKey、Lambda名などが表示されます。

実際にJSONをPOSTしてみる

ローカルのPCからJSONファイルをPOSTしてみようと思います
JSONファイルの中身はこんな感じ

sample.json
{
  "deviceID": "sample0002",
  "record_time": "2018-03-13T00:00:00.000+0900",
  "val_1": "testtesttest",
  "val_2": "1234567890",
  "val_3": "q2w3e4r5t6y7u8i"
}

CURLでPOSTします。
sample.jsonsample_post.json としてS3にPUTすることを想定しています。
※APIKeyを適用していないなら -H "X-API-KEY: APIKey" を削除してください。

$ curl -X POST --data @sample.json \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: APIKey" \
  https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/send/sample_post.json

{"message":"SUCCEEDED!","res":""}

成功すれば、上記のようにメッセージが返ってきて、S3に保存されていると思います。

さいごに

AWS非推奨ではありますが、APIKeyを利用して認証を行なっています。
本来であればAWS認証情報(Sig4)を生成してそれを元に認証を行うのですが、デバイスの中には認証情報を生成できるだけのリソースがないという場合が多々あります。
その場合にこのAPIKeyを利用してAWS側へリクエストを送信するという方法を取ることになります。
もう一度言っておきますが この方法は非推奨ですのでご注意ください

んじゃどうしたらいいの?というあなたに SORACOM です。
SORACOMのサービスを利用すれば、このような問題は一気に解決されます。
SORACOM側にAWSの認証情報を持っておくことができるので、デバイスには一切AWSの認証情報を持つ必要が無くなります。
デバイスからSORACOMへリクエストを送信して、SORACOM側であらかじめ設定しておいた認証情報を元にAWS認証情報を生成し、AWSにリクエストを送信します。
認証情報を変えたければコンソールから変えれば良いだけです。
うん、めっちゃ楽。

なんかSORACOMの宣伝みたいになってしまいましたが、IoTデバイスからAWS等のクラウドにリクエストを送信する際には便利なツールですので、ぜひお使いください。

ではまた!

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2