Покроковий посібник з імпорту CSV та Excel у мобільні додатки
Стикалися з ситуацією: очікуєте структурований CSV, а отримуєте файл, де колонки перейменовано, додано заголовок «Мої дані», кодування Windows-1251 і роздільник — кома, хоча раніше була крапка з комою? Ми, команда з 7-річним досвідом мобільної розробки, реалізували імпорт для понад 100 проєктів — від банківських застосунків до ритейл-рішень. Наше завдання — щоб будь-який файл завантажився без кражів і зі зрозумілими звітами про помилки. Економія бюджету на ручному введенні досягає 30–40% від витрат на персонал, що може сягати $5000 на рік. Вартість реалізації базового CSV-імпорту починається від $200. Працюємо на ринку з 2016 року, маємо понад 7 років досвіду та 100+ реалізованих проєктів. Зв'яжіться з нами для оцінки вашого проєкту.
Розбір вхідного файлу — найнепередбачуваніше
Визначення кодування
CSV приходить у UTF-8, UTF-8 з BOM, Windows-1251, CP866. Універсальне рішення — бібліотека визначення кодування: на Android juniversalchardet, на iOS — власний аналіз BOM + fallback на String.Encoding.windowsCP1251. Якщо кодування не визначено правильно — Клиент замість «Клієнт». Univocity-parsers на Android — один із найкращих варіантів для роботи з CSV, при цьому він в 2 рази швидше за opencsv на файлах понад 100 000 рядків.
Визначення роздільника
Парсер має пробувати ,, ;, \t і вибирати той, що дає найбільшу кількість однорідних колонок. Або дати користувачеві вибрати вручну — чесніше і надійніше.
Порожні рядки, дублі заголовків, змішані типи
Реальні користувацькі файли містять порожні рядки між блоками, об'єднані комірки в Excel, числа в колонці «дата». Кожен із цих випадків потрібно обробляти явно, а не падати з ArrayIndexOutOfBoundsException.
Як автоматично визначити кодування CSV?
На Android використовуємо juniversalchardet або ICU4C. На iOS — перевіряємо BOM, потім пробуємо найімовірніші кодування: UTF-8, Windows-1251, CP866. Якщо жодне не підходить, пропонуємо користувачеві вибрати вручну. Це покриває 99% випадків.
Чому важливий транзакційний запис?
Імпорт без транзакції — ризик отримати частково завантажені дані при збої. Транзакційний запис гарантує, що або всі рядки будуть записані, або жодна. На Android Room реалізує це через @Transaction, на iOS Core Data — через performAndWait. Як зазначено в документації Room, це стандартна практика.
Архітектура імпорту
Реалізація імпорту по кроках
Як реалізувати імпорт крок за кроком:
- Вибір файлу через DocumentPicker (Android) або UIDocumentPickerViewController (iOS).
- Парсинг за допомогою бібліотек (opencsv тощо).
- Валідація кожного рядка зі збором помилок.
- Транзакційний запис у базу даних.
Вибір файлу
Вибір файлу через DocumentPicker (Android) або UIDocumentPickerViewController (iOS).
// Android — DocumentPicker val launcher = registerForActivityResult( ActivityResultContracts.GetContent() ) { uri -> uri?.let { viewModel.importFile(it) } } launcher.launch("text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") // iOS — UIDocumentPickerViewController let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.commaSeparatedText, .spreadsheet]) picker.delegate = self present(picker, animated: true) Парсинг
На Android для CSV — opencsv або univocity-parsers (останній швидший на великих файлах і коректно обробляє екранування лапок). Для .xlsx — Apache POI XSSFWorkbook. На iOS для CSV — власний парсер через Scanner або CSVParser із SwiftCSV. Для .xlsx — CoreXLSX.
// Android, univocity-parsers val settings = CsvParserSettings().apply { isHeaderExtractionEnabled = true format.delimiter = ';' } val parser = CsvParser(settings) val rows: List<Record> = parser.parseAllRecords(inputStream.reader(Charsets.UTF_8)) Валідація та маппінг
Перед записом у базу — валідація кожного рядка. Не «впасти на першій помилці», а зібрати всі невалідні рядки та показати зведення: «Імпортовано 847 із 900 рядків. 53 рядки пропущено — некоректний формат дати в колонці D». Користувач має зрозуміти, що пішло не так, і виправити файл.
data class ImportResult( val imported: Int, val skipped: List<SkippedRow> ) data class SkippedRow(val line: Int, val reason: String) Запис у базу даних
Транзакція цілком — або все, або нічого. На Room:
@Transaction suspend fun importRows(rows: List<TransactionEntity>) { database.clearAll() database.insertAll(rows) } Для інкрементального імпорту (додати нові, оновити існуючі) — INSERT OR REPLACE з унікальним полем-ідентифікатором.
Порівняння форматів файлів
| Формат | Android парсер | iOS парсер | Особливості |
|---|---|---|---|
| CSV | opencsv / univocity-parsers | SwiftCSV / Scanner | Автовизначення кодування та роздільника |
| XLS | Apache POI HSSFWorkbook | — | Застарілий формат, рідко використовується |
| XLSX | Apache POI XSSFWorkbook | CoreXLSX | Підтримка макросів, великі обсяги |
Типові помилки та їх вирішення
| Проблема | Вирішення |
|---|---|
| Парсинг на main thread | Виконувати на фоновому потоці (CoroutineScope, DispatchQueue) |
| Не екрановані лапки в CSV | Використовувати парсер з обробкою екранування (univocity, CoreXLSX) |
| Неоднозначний формат дати | Детектувати за регулярними виразами або дати вибір користувачеві |
UI-патерни
Прогрес-бар із поточним рядком (Оброблено 3 412 із 10 000), скасування через Job.cancel() на Android / Task cancellation на iOS. Після імпорту — екран із підсумками: скільки додано, оновлено, пропущено з причинами.
Попередній перегляд перших 5–10 рядків перед підтвердженням імпорту — хороша UX-практика, яка знижує кількість «я не те завантажив».
Що входить у роботу під ключ
- Вибір файлу через DocumentPicker (CSV, XLS, XLSX)
- Автовизначення кодування та роздільника
- Валідація зі звітом про помилки по рядках
- Транзакційний запис у локальну БД
- UI прогресу та попереднього перегляду
- Підтримка скасування імпорту
Терміни
Базовий CSV-імпорт із фіксованою структурою: 1–1,5 дня. З автовизначенням формату, валідацією, попереднім переглядом та звітом про помилки: 3–4 дні. Вартість розробки варіюється від $200 до $500, але в середньому окупається за 2–3 місяці. Автоматичний імпорт заощаджує до 40% часу порівняно з ручним введенням. Замовте реалізацію імпорту — отримайте стабільний модуль за 3–4 дні. Отримайте консультацію щодо вашого проєкту.







