Современный фронтенд давно перестал быть просто "лицом" приложения. Мы переносим в браузер нейросети, обработку видео и криптографию. Но когда дело доходит до банальной аналитики файлов — например, локального парсинга тяжелого 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-инструмент в вашем браузере.
