Современный фронтенд давно перестал быть просто "лицом" приложения. Мы переносим в браузер нейросети, обработку видео и криптографию. Но когда дело доходит до банальной аналитики файлов — например, локального парсинга тяжелого Excel или Parquet-файла и выполнения SQL-запросов по ним — мы часто упираемся в ограничения JS-библиотек или вынуждены гонять данные на сервер.

Команда r7-consult решила задачу радикально: мы взяли наш C++17 движок excel_loader, скомпилировали его в WebAssembly и получили возможность выполнять полноценный SQL по локальным файлам прямо в браузере.

В этой статье разберем архитектуру решения wasm-sqlite-database, посмотрим, как C++ код дружит с JS, и покажем, как превратить браузер в локальный ETL-инструмент.


Репозиторий проекта

Проблема: JS vs Тяжелые таблицы

Обычно работа с табличными данными на клиенте выглядит так:

  1. Чистый JS (SheetJS и аналоги): Отлично для небольших файлов. Но если пользователь загружает XLSX на 50 Мб или Parquet, UI-поток блокируется, а потребление памяти улетает в космос.

  2. Отправка на бэкенд: Надежно, но долго. Плюс вопросы приватности (пользователь не хочет отправлять конфиденциальный отчет на чужой сервер просто чтобы отфильтровать пару строк).

  3. 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


Как это работает: Взгляд изнутри

Архитектура решения делится на три слоя:

  1. C++ Core: Здесь происходит вся магия. Парсинг форматов (используются нативные библиотеки), планировщик запросов, построение виртуальных таблиц и расчет статистики.

  2. WASM Interop (Emscripten): Прослойка, которая экспортирует классы ExcelLoaderEngine, Workbook и QueryResult в мир JavaScript.

  3. 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 &gt; 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-файлы читаются особенно эффективно благодаря колоночной природе, которая отлично ложится на векторные структуры внутри движка.


Для чего это можно использовать?

  1. Local-First Аналитика: Создание дашбордов, которые работают полностью офлайн. Данные пользователя не покидают его устройство.

  2. Валидация данных на клиенте: Проверка CSV/Excel файлов сложными SQL-правилами до загрузки на сервер.

  3. Конвертеры форматов: Открыть Parquet, отфильтровать SQL-ем и сохранить результат в JSON/CSV — всё в браузере.

  4. Плагины для офисных пакетов: Расширение функционала веб-редакторов (OnlyOffice, R7-Office) возможностью выполнять SQL по ячейкам.

Заключение

wasm-sqlite-database демонстрирует, что веб стал полноценной платформой для тяжелых вычислений. Использование C++ и WebAssembly позволяет перенести логику, которая раньше жила только на бэкенде или в десктопном софте, прямо к пользователю.

Это не просто «читалка Excel», это полноценный SQL-инструмент в вашем браузере.

Репозиторий проекта

При поддержки Slider Data

Комментарии (0)