Skip to content
malak.cloud
  • Kontakt
  • O mnie
  • Search Icon

malak.cloud

Cloud Native na co dzień

tytuł
Jak przesłać plik do S3 za pomocą API Gateway

Jak przesłać plik do S3 za pomocą API Gateway

16 listopada 2022

Nie zawsze możemy lub chcemy skorzystać z presigned URL aby umożliwić upload pliku do S3. Nie jest to oczywiście jedyny możliwy sposób. Dziś pokażę jak przesłać plik do S3 za pomocą API Gateway. Bezpośrednio.

Jak przesłać plik do S3 za pomocą API Gateway

Infrastrukturę stworzymy za pomocą cdk. Cały kod dostępny jest oczywiście na moim GitHub-ie.

Do przyrządzenia potrawy będziemy potrzebowali dwa główne składniki:

  • bucket S3
  • API Gateway REST API

oraz kilka przypraw:

  • CloudWatch LogGroup
  • rola IAM dla API Gateway
  • API Gateway Resource
  • API Gateway Method
  • integracja API z S3
  • obowiązkowo!!! tagi

Krok po kroku pokażę Wam jak przesłać plik do S3 za pomocą API Gateway.

Jedna rzecz którą trzeba brać pod uwagę. Na chwilę obecną, maksymalna wielkość payload dla API Gateway wynosi 10MB. Warto sprawdzić to tutaj.

Tworzymy zasoby

No to po kolei…

S3 Bucket

Na początku utworzymy bucket, w którym będą lądowały przesłane pliki.

    const bucket = new s3.Bucket(
      this, 
      'Filesbucket',
      {
          autoDeleteObjects: true,
          removalPolicy: cdk.RemovalPolicy.DESTROY,
          encryption: s3.BucketEncryption.S3_MANAGED,
          blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
      }
    )

Zakładam, że testujemy rozwiązanie. W związku z tym w linii 5 i 6 usuwamy bucket po usunięciu całego stacka. Tak, to jest takie proste przy pomocy cdk. W innym przypadku trzeba zrobić to tak.

Na produkcji zapewne będziecie chc

API Gateway REST API

Chyba najważniejsza część rozwiązania. API Gateway REST API.

const api = new apigateway.RestApi(
  this,
  'Api',
 {
  description: 'S3 Proxy Api',
  binaryMediaTypes: ['*/*'],
  minimumCompressionSize: 0,
  cloudWatchRole: true,
  endpointTypes: [apigateway.EndpointType.EDGE],
  deployOptions: {
    stageName: 'dev',
    tracingEnabled: true,
    accessLogDestination: new apigateway.LogGroupLogDestination(logGroup),
    accessLogFormat: apigateway.AccessLogFormat.clf(),
    loggingLevel: MethodLoggingLevel.INFO,
    metricsEnabled: true,        
  },
}
);

Zwróćcie uwagę na trzy ustawienia:

  • binaryMediaTypes – jeżeli chcemy przesyłać pliki binarne przez API Gateway, musimy tutaj podać rodzaj plików
  • minimumCompressionSize – rozmiar, od którego API Gateway będzie kompresował payload
  • cloudWatchRole – zostanie automatycznie utworzona rola CloudWatch dla API Gateway

Jeszcze słówko odnośnie binaryMediaTypes. Tu ustawiamy wszystkie przesyłane pliki jako binarne. Dla ułatwienia. W rzeczywistości zapewne to nie będzie dobre rozwiązanie, gdyż rzeczywiście wszystkie pliki, nawet z ustawieniami Content-Type:application/json będą traktowane jako binarne.

Pozostałe ustawienia można znaleźć tutaj.

Jeżeli chodzi o deployment, włączamy logi, metryki oraz X-Ray. Jeżeli chcecie zmienić coś jeszcze, wszystkie możliwości opisane są tu.

CloudWatch LogGroup

Tworzymy następnie LogGroup dla Access Logs z API.

const logGroup = new logs.LogGroup(this, "ApiGatewayAccessLogs");

IAM Role

API Gateway musi mieć oczywiście uprawnienia do uploadu plików do naszego buc keta S3. Poniżej tworzymy rolę z niezbędnymi uprawnieniami.

const apiExecuteRole = new iam.Role(
  this, 
  'api-gateway-s3-assume-role', {
  assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"),
  roleName: "API-Gateway-S3-Integration-Role",
});

bucket.grantPut(apiExecuteRole);

cdk utworzy dla nas następującą rolę:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Abort*",
                "s3:PutObject",
                "s3:PutObjectLegalHold",
                "s3:PutObjectRetention",
                "s3:PutObjectTagging",
                "s3:PutObjectVersionTagging"
            ],
            "Resource": "arn:aws:s3:::apigtws3stack-filesbucket536fd8f5-e4j5pag9ze3o/*",
            "Effect": "Allow"
        }
    ]
}

Użyjemy jej później, w momencie tworzenia integracji API z S3.

API Gateway Resource

Mamy już API, teraz nadszedł czas na utworzenie w nim zasobu. Podajemy tam „zmienną” {key}, którą wykorzystamy później. Będzie to nazwa pliku, który zostanie zapisany w S3.

const resource = api.root.addResource(
  "{key}"
);

API Integration

Przejdźmy teraz do utworzenia integracji API z S3.

const apiIntegration = new apigateway.AwsIntegration({
  service: "s3",
  integrationHttpMethod: "PUT",
  path: `${bucket.bucketName}/{key}`,
  options: {
    credentialsRole: apiExecuteRole,
    integrationResponses: [
      {
        statusCode: "200",
        responseParameters: {
          "method.response.header.Content-Type": "integration.response.header.Content-Type",
        },
      },
    ],
    requestParameters: {
      "integration.request.path.key": "method.request.path.key",
    },
  },
});

Aby zapisać pliki w S3, jako metodę wybieramy PUT. Ścieżka to nazwa naszego, utworzonego na początku, bucketa oraz nazwa pliku pobrana z URL-a.

Za pomocą parametru credentialsRole ustawiamy utworzoną wcześniej rolę.

Następnie za pomocą integrationResponses

integrationResponses: [ { statusCode: "200", responseParameters: { "method.response.header.Content-Type": "integration.response.header.Content-Type", }, }, ],

przekazujemy do odpowiedzi API wartość nagłówka ContentType.

response

Za pomocą requestParameters przekazujemy dalej nazwę naszego pliku

requestParameters: 
{ 
  "integration.request.path.key": "method.request.path.key", 
}

apigateway s3 request

API Gateway Method

Nadszedł czas na dodanie metody do naszego zasobu API. Jak się zapewne domyślacie, będzie to metoda PUT.

const putFileMethod = resource.addMethod(
  'PUT',
  apiIntegration,
  {
    methodResponses: [
      {
        statusCode: "200",
        responseParameters: {
          "method.response.header.Content-Type": true,
        },
      },
    ],
    requestParameters: {
      "method.request.path.key": true,
      "method.request.header.Content-Type": true,
    },
  }
);

W response zwrócimy nagłówek ContentType, a w requeście będziemy wymagali wartości key w path, oraz nagłówka http z wartością ContentType.

Tags

Tutaj dodamy sobie tagi do tworzonych zasobów. Polecam. Szczególnie w środowiskach, gdzie mamy możliwość zapomnienia w jakim celu stworzyliśmy konkretny zasób

Tags.of(this).add('Name', 'S3 Proxy Api');
Tags.of(this).add('Owner', 'przemek.malak@example.com');

Deployment i testy

Po wykonaniu cdk deploy na naszym koncie powinniśmy mieć następujące zasoby

API Gateway s3 zasoby

a przesłane na nasz endpoint pliki powinny wylądować w buckecie Filesbucket. 

Poniżej polecenie curl, które prześle plik jpg do naszego API i zapisze je w pliku o nazwie plik.pdf:

curl -X "PUT" "https://<API ID>.execute-api.eu-central-1.amazonaws.com/dev/plik.pdf" \
     -H 'Content-Type: image/jpeg' -F 'data=@nasz_plik.jpeg'

Możemy teraz sprawdzić czy plik rzeczywiście wylądował z buckecie za pomocą polecenia aws s3 ls s3://nazwa-bucketa

aws s3 ls

Działa. Już wiecie jak przesłać plik do S3 za pomocą API Gateway.

Kończymy

Wypada po sobie posprzątać. Jeżeli chcecie skasować wszystkie utworzone zasoby to wystarczy polecenie cdk destroy. Wszystko zostanie usunięte z Waszego konta.

To co zrobiliśmy nie zawsze będzie tym sposobem, który powinniśmy stosować w przypadku przesyłania plików do S3. Stwierdzę nawet, że rzadko będzie preferowanym sposobem. Mogą się jednak pojawić sytuacje kiedy będzie preferowaną, bądź nawet jedyną możliwą do zastosowania metodą.

Warto wiedzieć, że tak się też da.

Przypominam, kod rozwiązania można pobrać z GitHub.

 


AWS, CloudNative, Cookbook
AWS, CloudNative, Cookbook

Post navigation

PREVIOUS
Domain Storytelling
NEXT
AWS News – listopad 2022
Comments are closed.
Cześć. Nazywam się Przemek Malak. Dzięki za wizytę. Mam nadzieję, że to o czym piszę Cię zainteresowało. Jeżeli chcesz ze mną pogadać, najłatwiej będzie przez LinkedIn.

Losowe wpisy

  • Czym jest dla mnie Cloud Native

    20 listopada 2020
  • Czyścimy Dockera

    12 sierpnia 2017
  • API Gateway – Autoryzacja

    6 października 2017
  • Zombie apokalipsa w Łodzi

    14 lutego 2018
  • CI/CD za pomocą AWS Copilot

    2 grudnia 2021
  • Apps
  • AWS
  • CloudNative
  • Cookbook
  • Data
  • DEV
  • GCP
  • IoT
  • Istio
  • k8s
  • Security
  • Social
  • GitHub
  • LinkedIn
© 2023   All Rights Reserved.