Buffer: бинарные данные

Buffer — встроенный класс Node.js для работы с сырыми бинарными данными (байтами) вне движка V8, используется при работе с файлами, сетевыми потоками, криптографией и бинарными протоколами.

Зачем нужно

JavaScript в браузере работает со строками и числами; бинарные данные появились позже (ArrayBuffer). В Node.js бинарные данные критичны с первого дня: чтение файлов, TCP-сокеты, HTTP-тела, криптография — всё это байты. Buffer — аналог Uint8Array но с дополнительными удобными методами.

Где используется

  • Чтение файлов (fs.readFile) и работа с потоками (Stream)
  • Криптография (crypto.hash, crypto.randomBytes)
  • Кодирование/декодирование строк (UTF-8, Base64, hex)
  • Бинарные протоколы (TCP, WebSocket бинарные сообщения)
  • Загрузка файлов через Multer (req.file.buffer)

Основной контент

Создание Buffer

// Из строки
const buf1 = Buffer.from('Hello, Node.js');
const buf2 = Buffer.from('Привет', 'utf8');
const buf3 = Buffer.from('48656c6c6f', 'hex');     // из hex
const buf4 = Buffer.from('SGVsbG8=', 'base64');    // из base64

// Из массива байт
const buf5 = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 'Hello'

// Выделить буфер заданного размера (нули)
const buf6 = Buffer.alloc(10);       // 10 байт, заполнен нулями
const buf7 = Buffer.allocUnsafe(10); // быстрее, но может содержать старые данные

// Из ArrayBuffer/TypedArray
const arrayBuffer = new ArrayBuffer(16);
const buf8 = Buffer.from(arrayBuffer);

Конвертация

const buf = Buffer.from('Hello, World!', 'utf8');

// Buffer → строка
buf.toString('utf8');     // 'Hello, World!'
buf.toString('hex');      // '48656c6c6f2c20576f726c6421'
buf.toString('base64');   // 'SGVsbG8sIFdvcmxkIQ=='
buf.toString('ascii');    // для ASCII-только данных

// Buffer → JSON
JSON.stringify(buf);      // {"type":"Buffer","data":[72,101,108,...]}
buf.toJSON();             // то же самое

Операции с Buffer

const buf = Buffer.alloc(10);

// Запись
buf.write('Hello', 0, 'utf8');  // записать с позиции 0
buf.writeUInt32BE(12345, 0);    // записать 4-байтное число big-endian

// Чтение
const value = buf.readUInt32BE(0); // прочитать 4-байтное число

// Срез (разделяет память!)
const slice = buf.slice(0, 5);
slice[0] = 0xFF; // изменит и buf!

// Безопасная копия
const copy = Buffer.allocUnsafe(5);
buf.copy(copy, 0, 0, 5);

// Объединение
const combined = Buffer.concat([
  Buffer.from('Hello, '),
  Buffer.from('World!')
]);

Частые паттерны

// Base64 кодирование/декодирование
const encoded = Buffer.from('secret data').toString('base64');
const decoded = Buffer.from(encoded, 'base64').toString('utf8');

// Hex для хешей
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update('data').digest('hex');

// Генерация случайных байт (для токенов, соли)
const token = crypto.randomBytes(32).toString('hex'); // 64-символьный hex токен
const salt = crypto.randomBytes(16).toString('base64');

// Сравнение буферов (time-safe для crypto)
const isEqual = crypto.timingSafeEqual(buf1, buf2); // не уязвим к timing attack

Buffer и Streams

const { Readable } = require('stream');

// Buffer → Readable Stream
const readable = Readable.from(Buffer.from('hello'));

// Читать файл в Buffer
const fs = require('fs').promises;
const data = await fs.readFile('file.bin'); // возвращает Buffer
console.log(data.length, 'bytes');

Частые ошибки

  • Buffer.slice делит память — изменение среза изменяет оригинал; использовать Buffer.from(buf.slice(0, n)) для копии
  • allocUnsafe без заполнения — содержит случайные данные из памяти; не отправлять клиенту без записи данных
  • Строки вместо Buffer для бинарных данных — потери при конвертации; хранить бинарные данные как Buffer
  • Не указывать кодировкуtoString по умолчанию UTF-8, но from('hex string') нужно Buffer.from(str, 'hex')

Связанные темы

Ресурсы


🎓 Источник: Работа с файлами, буферами и файловыми потоками в Node.js

  • 📅 2018-10-10 · YouTube · eQGBS15vUac
  • Тезисы:
    • Buffer.alloc — это аналог malloc, выделяет и обнуляет память
    • Buffer.allocUnsafe — аналог выделения без очистки, быстрее, но содержит «хлам» из памяти процесса
    • Риск exploit: если отдать allocUnsafe-буфер без полной перезаписи — клиент получит чужие данные из памяти
    • Правило: для финансовых и чувствительных данных — только alloc, никогда allocUnsafe
    • Одни и те же данные можно вывести как hex / base64 / utf8 / binary — это разные представления одних байтов
  • Цитата:

    «alloc — это аналог malloc обычный. А allocUnsafe — аналог выделения памяти без очистки. Если буфер был заполнен нулями, то тут в памяти весь хлам, который остался.»


🎓 Источник: Архив 2018 — Часть 8 Типизированные массивы, буферы, итераторы

  • 📅 2020-01-06 · YouTube · bFT7VGFfP7o
  • Тезисы:
    • Node Buffer сейчас — обёртка над Uint8Array, до появления TypedArray в языке у ноды был свой Buffer; теперь они почти эквивалентны (Buffer быстрее, иначе копируется)
    • В основе всех TypedArray — ArrayBuffer; у Buffer/Uint8Array фиксированный byte-shift
    • Uint8ClampedArray — обрезает значения до 0–255, удобен для пикселей картинок
    • DataView позволяет читать/писать разной ширины по произвольному смещению
    • buffer.slice работает по байтам и не понимает Unicode — для строк это решает StringDecoder в стримах
    • По буферу можно итерироваться через for-of, entries даёт iterable
  • Цитата:

    «Буфер, который сейчас является личным представлением как Uint8Array. Такой же, но с некоторыми отличиями. Он быстрее, он чуть-чуть по-другому копируется.»

🎓 Источник: Работа с файлами, буферами и потоками

  • 📅 2018-10-10 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2018-10-10 — Работа с файлами, буферами и файловыми потоками в Node.js (eQGBS15vUac).md)
  • Тезисы:
    • Buffer.alloc(n) vs Buffer.allocUnsafe(n) — второй НЕ зануляет память → утечка чужих данных в новый буфер (быстрее, но опасно)
    • Буфер в разных кодировках: buf.toString('hex'), 'base64', 'utf8', 'binary'
    • Base64 — 6 бит на символ, +33% размера
    • Buffer.concat([buf1, buf2]) — склейка нескольких буферов в один
    • Буфер ведёт себя как строка: индексация, length, slice — но возвращает буфер, не строку
    • Buffer НЕ под GC V8 — отдельная C++ память, чтоб не давить на сборку мусора

🎓 Источник: Обзор встроенного Node.js API

  • 📅 2018-09-26 · YouTube
  • Тезисы: Buffer изменяемый по позициям, склейка чанков из сокета через индексы и Buffer.concat