Запускаем C++ SQL-движок в браузере: как парсить Excel, CSV и Parquet через WebAssembly (без сервер…
- четверг, 4 декабря 2025 г. в 00:00:06
Современный фронтенд давно перестал быть просто "лицом" приложения. Мы переносим в браузер нейросети, обработку видео и криптографию. Но когда дело доходит до банальной аналитики файлов — например, локального парсинга тяжелого Excel или Parquet-файла и выполнения SQL-запросов по ним — мы часто упираемся в ограничения JS-библиотек или вынуждены гонять данные на сервер.
Команда r7-consult решила задачу радикально: мы взяли наш C++17 движок excel_loader, скомпилировали его в WebAssembly и получили возможность выполнять полноценный SQL по локальным файлам прямо в браузере.
В этой статье разберем архитектуру решения wasm-sqlite-database, посмотрим, как C++ код дружит с JS, и покажем, как превратить браузер в локальный ETL-инструмент.
Обычно работа с табличными данными на клиенте выглядит так:
Чистый JS (SheetJS и аналоги): Отлично для небольших файлов. Но если пользователь загружает XLSX на 50 Мб или Parquet, UI-поток блокируется, а потребление памяти улетает в космос.
Отправка на бэкенд: Надежно, но долго. Плюс вопросы приватности (пользователь не хочет отправлять конфиденциальный отчет на чужой сервер просто чтобы отфильтровать пару строк).
AlaSQL: Хорошее JS-решение, но производительность на больших объемах уступает нативному коду.
Нам нужно было решение, которое объединяет скорость C++ и удобство SQL, но работает внутри браузерной песочницы.
В основе лежит 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 сразу sales_2024.xlsx и clients.csv. Движок позволит вам делать JOIN между листом Excel и CSV-файлом, как если бы это были таблицы в одной базе данных.
Давайте посмотрим, как это выглядит в коде. Никаких сложных конфигураций, всё работает прямо в браузере.
Нам не нужно парсить файл руками. Мы просто передаем 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;
Теперь, когда файл «примонтирован», мы можем обращаться к нему через 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);
}
Движок поддерживает работу через 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-инструмент в вашем браузере.