Продолжая тему нарезки картинок при помощи бессерверных решений, хочу предложить вашему вниманию дополнение в виде бессерверного 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. Давайте рассмотрим сколько будет стоит обслуживать такой проект:
Уровень бесплатного пользования AWS Lambda включает 1 млн бесплатных запросов в месяц и 400 000 ГБ-секунд вычислений в месяц
Уровень бесплатного использования API Gateway включает один миллион вызовов API HTTP, один миллион вызовов API REST, один миллион сообщений и 750 000 минут подключения в месяц на протяжении до 12 месяцев.
Новые клиенты AWS ежемесячно получают 5 ГБ хранилища Amazon S3 класса S3 Standard, 20 000 запросов GET, 2000 запросов PUT, COPY, POST, или LIST, а также 100 ГБ исходящего трафика.
Всегда бесплатно 1 ТБ исходящих данных 10 000 000 запросов HTTP или HTTPS 2 000 000 вызовов CloudFront Function
Итак, видно, что при умеренном использовании это все практически бесплатно (что подходит для большинства админ панелей и слабо нагруженных проектов).
Весь код доступен в публичных репозиториях simple-serverless-front, simple-serverless-back, lambda-ffmpeg-tifig