Современный фронтенд давно перестал быть просто "лицом" приложения. Мы переносим в браузер нейросети, обработку видео и криптографию. Но когда дело доходит до банальной аналитики файлов — например, локального парсинга тяжелого Excel или Parquet-файла и выполнения SQL-запросов по ним — мы часто упираемся в ограничения JS-библиотек или вынуждены гонять данные на сервер.
Команда r7-consult решила задачу радикально: мы взяли наш C++17 движок excel_loader, скомпилировали его в WebAssembly и получили возможность выполнять полноценный SQL по локальным файлам прямо в браузере.
В этой статье разберем архитектуру решения wasm-sqlite-database, посмотрим, как C++ код дружит с JS, и покажем, как превратить браузер в локальный ETL-инструмент.
Проблема: JS vs Тяжелые таблицы
Обычно работа с табличными данными на клиенте выглядит так:
Чистый JS (SheetJS и аналоги): Отлично для небольших файлов. Но если пользователь загружает XLSX на 50 Мб или Parquet, UI-поток бл��кируется, а потребление памяти улетает в космос.
Отправка на бэкенд: Надежно, но долго. Плюс вопросы приватности (пользователь не хочет отправлять конфиденциальный отчет на чужой сервер просто чтобы отфильтровать пару строк).
AlaSQL: Хорошее JS-решение, но производительность на больших объемах уступает нативному коду.
Нам нужно было решение, которое объединяет скорость C++ и удобство SQL, но работает внутри браузерной песочницы.
Решение: C++ Core + WASM
В основе лежит excel_loader — наш движок на C++17. Изначально он создавался для серверных задач и CLI, но благодаря Emscripten мы портировали его в веб.
Что он умеет:
Виртуализация таблиц: Файл не просто читается в память, он монтируется как виртуальная SQL-таблица.
Всеядность: Поддерживает не только «попсовые» CSV/XLSX, но и специфичные форматы вроде Parquet, DuckDB, и даже легаси (DBF, MDB).
Единый пайплайн:
Файл→Workbook (набор датасетов)→SQL-запрос→DataFrame.
Поддерживаемые форматы (из коробки)
Движок автоматически определяет формат по сигнатуре файла (input blob), фронтенду не нужно гадать, что именно загрузил пользователь:
Тип | Форматы |
|---|---|
Excel / Office | XLSX, XLSB, XLS, ODS |
Flat Files | CSV, TSV, TXT |
Columnar / BigData | Parquet, DuckDB, ORC, AVRO |
Legacy DB | SQLite (.db), DBF, MDB (Access), ACCDB |
NoSQL / Docs | JSON, JSONL, XML, HTML |
Как это работает: Взгляд изнутри
Архитектура решения делится на три слоя:
C++ Core: Здесь происходит вся магия. Парсинг форматов (используются нативные библиотеки), планировщик запросов, построение виртуальных таблиц и расчет статистики.
WASM Interop (Emscripten): Прослойка, которая экспортирует классы
ExcelLoaderEngine,WorkbookиQueryResultв мир JavaScript.Client JS: Тонкая обертка, которая забирает файл из
<input>, передаетArrayBufferв WASM и получает обратно JSON-результат.
Понятие Workbook (Рабочая книга)
Ключевая абстракция движка — Workbook. Это не просто один файл, это проект.
Вы можете загрузить в один Workbook сразу sales_2024.xlsx и clients.csv. Движок позволит вам делать JOIN между листом Excel и CSV-файлом, как если бы это были таблицы в одной базе данных.
Практика: API и пример кода
Давайте посмотрим, как это выглядит в коде. Никаких сложных конфигураций, всё работает прямо в браузере.
1. Инициализация и загрузка файла
Нам не нужно парсить файл руками. Мы просто передаем Uint8Array в движок.
// Инициализация WASM-модуля const module = await ExcelLoaderModule(); const engine = new module.ExcelLoaderEngine(); // Получаем файл из стандартного input const fileInput = document.getElementById("fileUploader"); const file = fileInput.files[0]; const arrayBuffer = await file.arrayBuffer(); // Магия: открываем файл как Workbook // Движок сам поймет, xlsx это или parquet const workbook = engine.openSingleFile( file.name, new Uint8Array(arrayBuffer) ); // Сохраняем ссылку для запросов window.currentWorkbook = workbook;
2. Выполнение SQL-запроса
Теперь, когда файл «примонтирован», мы можем обращаться к нему через SQL. Если в Excel-файле есть лист Sheet1, мы можем его фильтровать и агрегировать.
const sql = "SELECT * FROM 'Sheet1' WHERE Amount > 1000 ORDER BY Date DESC LIMIT 10"; try { // Выполнение запроса внутри WASM const result = window.currentWorkbook.executeQuery(sql); // Преобразование результата в JSON для фронтенда const data = result.toJSON(); console.table(data); } catch (e) { console.error("SQL Error:", e); }
3. Работа с несколькими файлами (Manifest)
Движок поддерживает работу через JSON-манифесты. Это позволяет описать структуру проекта, дать таблицам псевдонимы и собрать сложный отчет из разрозненных файлов.
// manifest.json { "files": [ { "name": "data_2023.parquet", "alias": "History" }, { "name": "new_orders.csv", "alias": "Current" } ] }
// Загрузка проекта по манифесту const workbook = openProjectFromManifest(engine, manifestJson, fileBuffers); // Теперь доступен: SELECT * FROM History JOIN Current ...
Производительность и Метаданные
Помимо сырых данных, движок предоставляет богатые метаданные через метод getStats() или просмотр датасетов. Это полезно для UI, чтобы показать пользователю структуру файла до выполнения "тяжелых" запросов.
Пример ответа метаданных:
{ "name": "sheet1$", "displayName": "Отчет_Продажи", "fileName": "report_final.xlsx", "type": "excel-sheet", "rows": 15400, "columns": 24, "memoryUsage": "..." }
Все операции выполняются в памяти (In-Memory). Благодаря C++, накладные расходы на структуры данных минимальны по сравнению с объектами V8 (JS). Parquet-файлы читаются особенно эффективно благодаря колоночной природе, которая отлично ложится на векторные структуры внутри движка.
Для чего это можно использовать?
Local-First Аналитика: Создание дашбордов, которые работают полностью офлайн. Данные пользователя не покидают его устройство.
Валидация данных на клиенте: Проверка CSV/Excel файлов сложными SQL-правилами до загрузки на сервер.
Конвертеры форматов: Открыть Parquet, отфильтровать SQL-ем и сохранить результат в JSON/CSV — всё в браузере.
Плагины для офисных пакетов: Расширение функционала веб-редакторов (OnlyOffice, R7-Office) возможностью выполнять SQL по ячейкам.
Заключение
wasm-sqlite-database демонстрирует, что веб стал полноценной платформой для тяжелых вычислений. Использование C++ и WebAssembly позволяет перенести логику, которая раньше жила только на бэкенде или в десктопном софте, прямо к пользователю.
Это не просто «читалка Excel», это полноценный SQL-инструмент в вашем браузере.
