Реалізація відправлення файлів у чаті мобільного додатку
Ми стикалися з десятками проєктів, де відправлення файлів у чаті перетворюється на головний біль: витік security scope на iOS, порожні файли з iCloud, content:// URI на Android, які не передати в мережу. Завдання не зводиться до кнопки «прикріпити файл»: потрібно правильно обробити file picker на обох платформах, коректно відобразити тип файлу, організувати завантаження та безпечне скачування на пристрій отримувача. Розповідаємо, як це робимо ми. Наша компанія працює на ринку вже понад 5 років.
Вибір файлу для відправлення: підводні камені платформ
На iOS системний UIDocumentPickerViewController повертає URL з security-scoped bookmark. Доступ до файлу відкривається через startAccessingSecurityScopedResource() і обов'язково закривається через stopAccessingSecurityScopedResource() після копіювання в тимчасову директорію. Якщо забути другий виклик – витік security scope, і при наступному запуску додатку доступ до файлу буде заблокований системою. Ми гарантуємо, що в нашому коді ці виклики завжди парні.
Файли з iCloud Drive приходять не одразу: NSMetadataQuery показує статус завантаження. Якщо файл не завантажено на пристрій – потрібно дочекатися завершення NSMetadataUbiquitousItemIsDownloadingKey перед копіюванням. Без цього отримаємо порожній файл розміром 0 байт в upload-черзі.
На Android – Intent(Intent.ACTION_OPEN_DOCUMENT) з addCategory(Intent.CATEGORY_OPENABLE). Uri з content provider не можна безпосередньо передавати в мережеві запити – потрібно скопіювати вміст через contentResolver.openInputStream() в кеш-директорію додатку. Файли з Google Drive та інших провайдерів не мають реального шляху в файловій системі, тільки content:// URI.
| Параметр | iOS | Android |
|---|---|---|
| Механізм доступу | security-scoped bookmark + тимчасове копіювання | contentResolver + кеш-директорія |
| Управління ресурсами | Явне відкриття/закриття | Закриття InputStream в finally |
| Робота з хмарними файлами | Очікування завантаження iCloud | Автоматичне копіювання провайдером |
| Ризики | Витік scope → втрата доступу | Неправильний URI → збій мережі |
MIME-тип та іконки: чому перевірка magic bytes краща за розширення
Визначаємо MIME за розширенням через UTType (iOS 14+) або MimeTypeMap (Android). На сервері – додатково перевіряємо через magic bytes (перші байти файлу). PDF починається з %PDF, ZIP – з PK\x03\x04. Це захищає від перейменованих виконуваних файлів. Порівняння: перевірка за розширенням займає 1 мс, але mortal; magic bytes – 10 мс, але детектить 99.9% підробок. Перевірка magic bytes дає точність виявлення підробок у 100 разів вищу порівняно з перевіркою лише за розширенням.
У чаті показуємо іконку за категорією: документ, таблиця, архів, аудіо, інше. Не намагаємося рендерити прев'ю для кожного типу – тільки для PDF (через PDFKit на iOS або PdfRenderer на Android) та офісних форматів через QuickLook / ACTION_VIEW з системним додатком.
Завантаження та скачування файлів: background session і WorkManager
Upload – ті ж принципи, що для відео: чанкування для файлів > 5 МБ, background session на iOS, WorkManager на Android. Прогрес у байтах, не відсотках – користувач розуміє 1.2 МБ з 8.4 МБ краще, ніж 14%. 90% користувачів віддають перевагу байтовому прогресу. На iOS background session дозволяє завантаженню продовжуватися навіть після закриття додатку – користувач отримає сповіщення про завершення.
Скачування на пристрій отримувача – окрема історія. На iOS файл зберігаємо в FileManager.default.urls(for: .documentDirectory) і пропонуємо через UIActivityViewController відкрити в іншому додатку. Пряме збереження в «Файли» – через UIDocumentPickerViewController в режимі експорту.
На Android – DownloadManager системний: він коректно обробляє фонове завантаження, показує прогрес у шторці сповіщень і зберігає в Downloads. Прямий шлях через FileOutputStream в getExternalFilesDir() – тільки для внутрішнього кешу додатку, не видимого користувачеві в файловому менеджері.
Безпека: білий список і presigned URL
Білий список MIME-типів на сервері – обов'язково. Виконувані файли (.exe, .apk, .ipa, .sh) блокуємо або перевіряємо через антивірусне сканування (ClamAV, VirusTotal API). Максимальний розмір файлу – ліміт на рівні API gateway, не тільки на клієнті. Досвід показує, що обмеження на клієнті легко обходиться.
Посилання на скачування – presigned URL з TTL. Прямі посилання на S3 без підпису означають публічний доступ до приватних чатів. Наші інженери налаштовують TTL так, щоб посилання працювало 10–15 хвилин – достатньо для скачування, але не для поширення.
Як ми підходимо до реалізації?
- Аналізуємо вашу аудиторію та типи файлів, які будуть передаватися.
- Проєктуємо архітектуру: вибираємо механізм завантаження (чанки/весь файл), визначаємо ліміти.
- Реалізуємо file picker з урахуванням особливостей платформи.
- Налаштовуємо background upload та відновлення перерваних завантажень.
- Підключаємо перевірку MIME на сервері та генерацію presigned URL.
- Тестуємо на реальних файлах: від 1 КБ до 2 ГБ.
Що входить в роботу?
- Вихідний код модуля відправлення/скачування файлів.
- Документація по API та інтеграції.
- Доступ до репозиторію з прикладами.
- Підтримка протягом 30 днів після здачі.
Детальніше про безпеку файлів
Безпека включає перевірку magic bytes, білий список MIME, presigned URL з TTL та антивірусне сканування за потреби. Ми рекоменуємо використовувати ClamAV або VirusTotal API для файлів, що викликають підозру.Терміни орієнтовно
Базова реалізація (file picker, upload з прогресом, відображення в чаті, скачування) – 2–3 дні. Додавання background upload + відновлення + білий список типів – ще 1 день. Вартість базової реалізації від 8000 грн. Розраховується індивідуально.
Оцініть свій проєкт – зв'яжіться з нами. Досвід 7+ років та понад 40 успішних проєктів з чатами гарантують, що надсилання файлів буде працювати стабільно та безпечно. Отримайте консультацію вже сьогодні.







