Інтеграція AWS S3 для зберігання файлів мобільного додатку
Коли користувач завантажує аватар або документ через мобільний додаток, перший вопрос — куди це відправити. Зберігати бінарники в базі даних — нікому в здоровому розумі не робить. Передавати через власний бекенд — означає гонити мегабайти через зайвий hop та платити за трафік двічі. AWS S3 з presigned URL вирішує це елегантно: клієнт завантажує напрямку в S3, бекенд видає тільки тимчасовий токен.
Як влаштована правильна інтеграція
Стандартна схема: мобільний клієнт запитує у бекенда presigned URL, отримує його, робить PUT-запит прямо в S3, й тільки потім повідомляє бекенд, що файл завантажений. Бекенд ніколи не бачить байти файлу — тільки метадані.
На iOS використовуємо AWS SDK for Swift (aws-sdk-swift) або більш lightweight варіант — AWSS3 через Amplify. На Android — AWS SDK for Kotlin або Amplify Android. Для React Native — @aws-sdk/client-s3 з офіційного JS SDK v3.
// iOS: завантаження через presigned URL без SDK — через URLSession
let presignedURL = URL(string: urlFromBackend)!
var request = URLRequest(url: presignedURL)
request.httpMethod = "PUT"
request.setValue("image/jpeg", forHTTPHeaderField: "Content-Type")
let uploadTask = URLSession.shared.uploadTask(with: request, fromFile: localFileURL) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
// retry logic тут
return
}
// Сповіщаємо бекенд про успішну завантаження
self.confirmUpload(fileKey: fileKey)
}
uploadTask.resume()
На Android через OkHttp або Retrofit — аналогічно. Головне — не забути про Content-Type у заголовку: S3 перевіряє його совпадіння з тим, що було зазначено при генерації presigned URL. Розхідження дає 403.
Де чаще всього виникають проблеми
Срок дії presigned URL. За замовчуванням — 15 хвилин. Якщо користувач на повільному з'єднанні або додаток довго чекав вибору файлу, URL просрочиться ще до початку завантаження. Практика: генерувати URL безпосередньо перед PUT-запитом, а не у момент відкриття picker.
CORS для веб-превью. Якщо ті ж файли потрібно показувати у WebView або на сайті, S3 bucket вимагає налаштування CORS policy. Без неї браузер блокує запит, хоча мобільний клієнт працює нормально — URLSession та OkHttp CORS не перевіряють.
Мультипарт для великих файлів. S3 Multipart Upload потрібен від 5 МБ. Для відео це обов'язково. AWS Amplify Storage абстрагує це автоматично через Amplify.Storage.uploadFile, але при ручній реалізації через presigned URL мультипарт вимагає окремого API: CreateMultipartUpload → UploadPart × N → CompleteMultipartUpload.
// Android: використання Amplify для автоматичного multipart
Amplify.Storage.uploadFile(
StoragePath.fromString("uploads/${userId}/${filename}"),
localFile,
StorageUploadFileOptions.builder()
.contentType("video/mp4")
.build(),
progress -> Log.i("Upload", "Progress: ${progress.fractionCompleted}"),
result -> confirmUpload(result.path),
error -> handleUploadError(error)
)
Права доступу через IAM. Bucket policy повинна дозволяти s3:PutObject тільки для конкретних prefix-ів. Ніяких wildcard s3:* на весь bucket у production. Використовуйте Resource-based policy + IAM role з мінімальними правами.
Структура файлів та життєвий цикл
Заздалегідь продумана структура ключів у S3 рятує від головної болі пізніше:
uploads/{userId}/avatar/{timestamp}.jpg
uploads/{userId}/documents/{uuid}.pdf
public/content/{category}/{slug}.webp
temp/{sessionId}/{filename} ← очищується lifecycle rule через 24h
S3 Lifecycle Rules дозволяють автоматично видаляти тимчасові файли та переносити рідко використовувані у S3 Glacier для економії. Налаштовується через Terraform або AWS Console — ничого в коді додатку.
CloudFront перед S3
Видавати медіафайли напрямку з S3 — нормально для старту. Але для production з користувачами в різних регіонах стоїть поставити CloudFront. Це CDN з edge nodes, який кешує файли ближче до користувача та знижує latency при завантаженні зображень. На iOS це ощутимо при завантаженні thumbnail-листів: без CDN кожен запит йде у us-east-1, з CDN — з ближайшого edge.
Для presigned URL з CloudFront потрібен CloudFront Signed URL (не S3 presigned) — інший механізм з іншою логікою підпису.
Термін
Базова інтеграція (presigned upload + download, одна платформа): 3–5 днів. Повна реалізація з мультипарт, progress tracking, retry, lifecycle rules, CloudFront: 2–3 тижні. Вартість розраховується індивідуально.







