Продолжая тему нарезки картинок при помощи бессерверных решений, хочу предложить вашему вниманию дополнение в виде бессерверного frontend и backend. 

AWS S3 предлагает возможность загружать картинки напрямую в S3 из приложения. Архитектурно это выглядит вот так:

Я же предлагаю реализовать Frontend, Backend, а также нарезку картинок, описанную в этой статье более подробно, при помощи бессерверное решений. Итак, полная архитектура будет выглядеть вот так:

Для реализации этого для начала напишем простой backend, который будет создавать подписанную ссылку на загрузку картинок:

const {S3Client, PutObjectCommand} = require('@aws-sdk/client-s3');
const {v4} = require("uuid");

const s3 = new S3Client({
    signatureVersion: 'v4',
    region: config.REGION
});

const S3PutObjectCommand = (Key) => {
    return new PutObjectCommand({
        ACL: 'public-read',
        Bucket: config.S3_BUCKET,
        Key,
    });
}
const getS3SignedUrl = (name) => {
    const command = S3PutObjectCommand(name)
    return getSignedUrl(s3, command, {expiresIn: 600})
}
const getFileName = (key) => `${key}.jpg`;

const generateFileKey = () => {
    return v4()
}

module.exports.handler = async () => {
    const key = generateFileKey()
    const fileName = getFileName(key)
    const url = await getS3SignedUrl(fileName);
    return {
        statusCode: 200,
      	// заголовки для CORS. 
        // В реальном приложении их нужно поправить так, 
        // чтоб загрузка была разрешена только с определенных хостов
        headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Credentials': true,
        },
        body: JSON.stringify({key, url, fileName})
    };
}

Обернем этот код в Lambda и ApiGateway при помощи Serverless Framework. 

service: service-simple-serverless-back
frameworkVersion: '2 || 3'
app: simple-serverless-back

package:
  patterns:
    - '!node_modules/aws-sdk'
    - '!node_modules/serverless'
    - '!node_modules/@serverless'
    - '!.idea'
    - '!images'
    - '!yarn.lock'
    - '!yarn.error.log'
    - '!README.md'
    - '!.gitignore'
    - '!package.json'
    - '!.git'

custom:
  name: 'simple-serverless-back'
  environment: 'prod'
  region: 'us-east-1'
  # зададим бакет в который мы будем загружать файлы, 
  # его также можно пробросить через переменные окружения
  bucket: 'service-lambda-thumb-tifig-d-sourcebucketdc914398-10oo755kbh9e8'
  lambda_prefix: ${self:custom.environment}-${self:custom.name}

provider:
  name: aws
  lambdaHashingVersion: '20201221'
  environment:
    S3_BUCKET: ${self:custom.bucket}
    REGION: ${self:custom.region}
  region: ${self:custom.region}
  runtime: nodejs14.x
  iamRoleStatements:
    - Effect: Allow
      Action:
        - s3:PutObject
        - s3:PutObjectAcl
        - s3:GetObject
      Resource:
        - 'arn:aws:s3:::${self:custom.bucket}'
        - 'arn:aws:s3:::${self:custom.bucket}/*'

functions:
  getS3SignedUrl:
    name: get-s3-signed-url
    handler: index.handler
    events:
      - http:
          method: GET
          path: /
          cors: true

Делаем sls deploy и получаем вот такую лямбду:

Напишем простой Frontend, который берет подписанную ссылку по запросу на backend и загружает выбранный файл в с3. Создадим новое React приложение:

npx create-react-app app

Код загрузки файла:

import React, {useCallback} from 'react';
import {getSignedUrlApi, uploadToS3Api} from '../utils/api';

export const Upload = ({onSetUploadImages}) => {
  
    const onChange = useCallback(async (e) => {
      
        let files = e.target.files
          
        // берем подписанную ссылку
        const signedUrlResults = await getSignedUrlApi()
        const {url, fileName} = signedUrlResults
        
        // загружаем файл в S3
        await uploadToS3Api(url, files[0])
        onSetUploadImages(fileName)
      
    }, [onSetUploadImages])

    return (
        <div className="upload">
            <div>
                <input type="file" onChange={onChange} />
            </div>
        </div>
    );
}

Весь код приложения доступен в репозитории

Также завернем это все через Serverless Framework в статически S3 сайт при помощи супер короткого конфига:

service: service
provider:
  name: aws

plugins:
  - serverless-lift

constructs:
  front:
    type: static-website
    path: 'build'

Делаем yarn build и sls deploy. Попробуем загрузить картинку и посмотреть на результат:

В итоге мы получили полностью бессерверное решение backend+frontend+image convertor. Давайте рассмотрим сколько будет стоит обслуживать такой проект:

Цены на Lambda:

Уровень бесплатного пользования AWS Lambda включает 1 млн бесплатных запросов в месяц и 400 000 ГБ-секунд вычислений в месяц

Цены на Api Gateway

Уровень бесплатного использования API Gateway включает один миллион вызовов API HTTP, один миллион вызовов API REST, один миллион сообщений и 750 000 минут подключения в месяц на протяжении до 12 месяцев.

Цены на S3

Новые клиенты AWS ежемесячно получают 5 ГБ хранилища Amazon S3 класса S3 Standard, 20 000 запросов GET, 2000 запросов PUT, COPY, POST, или LIST, а также 100 ГБ исходящего трафика.

Цены на Cloudfront

Всегда бесплатно 1 ТБ исходящих данных 10 000 000 запросов HTTP или HTTPS 2 000 000 вызовов CloudFront Function

Итак, видно, что при умеренном использовании это все практически бесплатно (что подходит для большинства админ панелей и слабо нагруженных проектов).

Весь код доступен в публичных репозиториях simple-serverless-front, simple-serverless-back, lambda-ffmpeg-tifig

Комментарии (0)