Імплементація завантаження файлів (Upload) у мобільному застосунку
Завантаження файлу з мобільного пристрою виглядає як проста задача до першого краху на Android 10+ через зміну URI у FileProvider, або до першого відхиленого review в App Store через неправильний запит дозволів на медіатеку. Розберемось, що важливо зробити правильно.
Типові проблеми при реалізації
На Android основна біль — робота з content:// URI замість прямих шляхів до файлу. З Android 10 (API 29) прямий доступ до файлової системи обмежений, і спроби передати file:// шлях напрямку в Retrofit/OkHttp приводять до FileUriExposedException. Правильний підхід — читати файл через ContentResolver.openInputStream() і передавати потік, а не шлях.
На iOS доступ до фотобіблиотеки вимагає правильного Info.plist: NSPhotoLibraryUsageDescription для iOS 13 і нижче, PHPickerViewController для iOS 14+ (не вимагає дозволів на весь фотоальбом). Використання застарілого UIImagePickerController з .photoLibrary у 2024 році — прямий шлях до замічання від ревюєра Apple.
Реалізація
Multipart upload через Retrofit на Android:
@Multipart
@POST("upload")
suspend fun uploadFile(
@Part file: MultipartBody.Part,
@Part("description") description: RequestBody
): Response<UploadResponse>
// виклик:
val requestBody = file.asRequestBody("image/*".toMediaTypeOrNull())
val part = MultipartBody.Part.createFormData("file", file.name, requestBody)
Для великих файлів (відео, архіви) — потокова передача через RequestBody з переозначеним writeTo, щоб не грузити весь файл у пам'ять.
На iOS використовуємо URLSession.uploadTask(with:from:) або Alamofire:
AF.upload(
multipartFormData: { form in
form.append(fileURL, withName: "file")
},
to: "https://api.example.com/upload"
).uploadProgress { progress in
print(progress.fractionCompleted)
}.responseDecodable(of: UploadResponse.self) { response in
// обробляємо
}
Flutter: пакет dio з FormData і MultipartFile.fromFile(). Прогрес через onSendProgress.
Важливо окремо обробити прогрес завантаження — користувач має бачити ProgressBar/LinearProgressIndicator з реальними відсотками, а не спіннер. Скасування завантаження реалізується через CancellationToken (Kotlin) або Task (Swift/Alamofire).
Що входить у роботу
Вибір підходу (multipart, presigned S3 URL, chunked upload) залежить від розміру файлів та інфраструктури. Реалізуємо піккер файлів під платформу, обробку дозволів, відображення прогресу, повтор при помилці мережі.
Строк: 1–2 дні для стандартного випадку, до 4 днів якщо потрібен chunked upload з resumable поведінкою.







