Будуємо безпечну систему завантаження файлів за допомогою попередньо підписаних URL від AWS S3, React та Node.js

pic

Вступ

У сучасних веб-додатках важливо ефективно та безпечно обробляти завантаження файлів. AWS S3 (Simple Storage Service) пропонує надійне рішення для зберігання файлів, а використання попередньо підписаних URL додає додатковий рівень безпеки, спрощуючи процес завантаження. У цьому детальному посібнику ми створимо повну систему керування файлами з відслідковуванням прогресу завантаження, функціональністю завантаження та чистим інтерфейсом користувача.

Розуміння попередньо підписаних URL

Попередньо підписані URL — це тимчасові URL-адреси, які надають специфічні дозволи для виконання операцій з об'єктами S3. Замість того, щоб давати клієнтам прямий доступ до вашого бакету S3, ви генеруєте тимчасові URL-адреси, які з часом стають недійсними.

Переваги:

  • Дозволяють користувачам завантажувати файли без надання їм облікових даних AWS
  • Надають тимчасовий, безпечний доступ для виконання конкретних операцій
  • Створюють можливості для завантаження файлів на клієнтській стороні веб-додатків

Передумови:

  • Встановлений Node.js
  • Обліковий запис AWS
  • Базові знання React та Express

Налаштування AWS

1. Створіть бакет S3

  1. Перейдіть до S3 в AWS Console
  2. Натисніть Create bucket
  3. Налаштуйте бакет:
  • Введіть унікальне ім’я бакету

pic

  • Виберіть регіон (не забудьте це для вашого додатку)
  • Зніміть позначку з "Block all public access" (необхідно для попередньо підписаних URL)
  • Увімкніть версіонування (рекомендується)

pic

  • Натисніть “Create bucket”

2. Налаштування CORS для бакету

  • Виберіть ваш бакет
  • Перейдіть на вкладку “Permissions”
  • Прокрутіть до “Cross-origin resource sharing (CORS)”
  • Натисніть “Edit” і додайте таку конфігурацію:
[  
 {  
 "AllowedHeaders": ["*"],  
 "AllowedMethods": ["PUT", "GET", "POST", "DELETE", "HEAD"],  
 "AllowedOrigins": ["*"],   
 "ExposeHeaders": ["ETag"]  
 }  
]

pic

3. Створіть користувача IAM для доступу

  • Перейдіть до IAM в AWS Console
  • Натисніть UsersAdd user
  • Налаштування користувача:
  • Ім’я користувача: s3-presigned-user-medium

pic

  • Виберіть Access key — Programmatic access
  • Встановіть дозволи
  • Знайдіть і виберіть AmazonS3FullAccess (Для продакшн-середовища створіть користувацьку політику з обмеженим доступом)
  • Перегляньте та створіть користувача

pic

pic

pic

pic

  • ВАЖЛИВО: Завантажте або скопіюйте Access Key ID та Secret Access Key

4. Налаштування середовища

Створіть файл .env у вашому проекті:

AWS_ACCESS_KEY_ID=your_access_key_id  
AWS_SECRET_ACCESS_KEY=your_secret_access_key  
AWS_REGION=your_region  
AWS_BUCKET_NAME=your_bucket_name

Початкове налаштування:

// Створіть директорії проекту  
mkdir s3-file-uploader  
cd s3-file-uploader  

// Створіть директорію для бекенду  
mkdir server  
cd server  
npm init -y  

// Встановіть залежності для бекенду  
npm install express @aws-sdk/client-s3 @aws-sdk/s3-request-presigner dotenv cors  

// Створіть фронтенд  
cd ..

npx create-react-app client  
cd client  
npm install axios

Реалізація бекенду

Створіть файл app.js у директорії серверу:

import express from 'express';  
import { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';  
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';  
import dotenv from 'dotenv';  
import cors from 'cors';  

dotenv.config();  
const app = express();  
app.use(cors());  
app.use(express.json());  

const s3Client = new S3Client({  
 region: process.env.AWS_REGION,  
 credentials: {  
 accessKeyId: process.env.AWS_ACCESS_KEY_ID,  
 secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,  
 },  
});

Ендпоінт для генерації URL для завантаження

app.post('/api/generate-upload-url', async (req, res) => {  
 try {  
 const { fileName, fileType } = req.body;  
 const command = new PutObjectCommand({  
 Bucket: process.env.AWS_BUCKET_NAME,  
 Key: `uploads/${fileName}`,  
 ContentType: fileType,  
 });  

 const uploadURL = await getSignedUrl(s3Client, command, { expiresIn: 3600 });  
 res.json({ uploadURL });  
 } catch (error) {  
 console.error('Помилка при генерації URL для завантаження:', error);  
 res.status(500).json({ error: 'Не вдалося створити URL для завантаження' });  
 }  
});

Цей ендпоінт:

  • Приймає fileName і fileType у тілі запиту
  • Створює попередньо підписаний URL, який дійсний протягом 1 години (3600 секунд)
  • Повертає URL, який фронтенд може використати для прямого завантаження на S3

Ендпоінт для переліку файлів

app.get('/api/files', async (req, res) => {  
 try {  
 const command = new ListObjectsV2Command({  
 Bucket: process.env.AWS_BUCKET_NAME,  
 Prefix: 'uploads/'  
 });  

 const { Contents } = await s3Client.send(command);  
 const files = Contents  
 .filter(item => item.Size > 0)  
 .map(item => ({  
 name: item.Key.replace('uploads/', ''),  
 size: item.Size,  
 lastModified: item.LastModified  
 }));  

 res.json(files);  
 } catch (error) {  
 console.error('Помилка при переліку файлів:', error);  
 res.status(500).json({ error: 'Не вдалося отримати список файлів' });  
 }  
});

Цей ендпоінт:

  • Переліковує всі файли в бакеті S3, що мають префікс ‘uploads/’
  • Фільтрує порожні файли/папки
  • Повертає спрощену інформацію про файли, включаючи ім’я, розмір та дату останнього редагування

Генерація URL для завантаження:

app.get('/api/generate-download-url/:fileName', async (req, res) => {  
 try {  
 const { fileName } = req.params;  
 const command = new GetObjectCommand({  
 Bucket: process.env.AWS_BUCKET_NAME,  
 Key: `uploads/${fileName}`,  
 ResponseContentDisposition: `attachment; filename="${fileName}"`,  
 });  

 const downloadURL = await getSignedUrl(s3Client, command, { expiresIn: 3600 });  
 res.json({ downloadURL });  
 } catch (error) {  
 console.error('Помилка при генерації URL для завантаження:', error);  
 res.status(500).json({ error: 'Не вдалося створити URL для завантаження' });  
 }  
});

Цей ендпоінт:

  • Приймає fileName як параметр URL
  • Створює попередньо підписаний URL для завантаження конкретного файлу
  • Встановлює параметр content-disposition, щоб завантаження відбулося у браузері
  • Повертає URL, який дійсний протягом 1 години

Розробка фронтенду

(Ось доступний вихідний код. Ви можете перевірити код фронтенду тут.)

Для завантаження:

  • Фронтенд надсилає інформацію про файл для генерації URL для завантаження
  • Бекенд генерує попередньо підписаний URL
  • Фронтенд використовує цей URL для прямого завантаження на S3
  • Прогрес завантаження відстежується на фронтенді

Для переліку файлів:

  • Фронтенд викликає ендпоінт для файлів
  • Бекенд переліковує об'єкти з S3
  • Фронтенд відображає список файлів

Для завантаження:

  • Фронтенд запитує URL для завантаження конкретного файлу
  • Бекенд генерує попередньо підписаний URL
  • Фронтенд ініціює завантаження, використовуючи цей URL

pic

Ця реалізація забезпечує безпечний та ефективний спосіб обробки завантаження та завантаження файлів з використанням AWS S3.
Використання попередньо підписаних URL забезпечує безпеку при збереженні хороших показників продуктивності та досвіду користувача.

Дякую за увагу!

Перекладено з: Building a Secure File Upload System with AWS S3 Pre-signed URLs, React, and Node.js

Leave a Reply

Your email address will not be published. Required fields are marked *