Перетащить обработку бинарных данных из Node.js в браузер — берешь Buffer, оборачиваешь в Uint8Array и production падает. Проблема в том, что Buffer — глобал из Node.js, его нет в браузере, а полифилы тащат лишние килобайты. Современный путь — Web Streams API и TextEncoder/TextDecoder, но есть грабли, которые ломают production.
Грабли 1. slice не уважает UTF-8
В Node.js
Buffer.from('Привет!').slice(0, 5) дает 5 байт и строку 'Прив'. В браузере new TextEncoder().encode('Привет!').slice(0, 5) режет посреди многобайтового символа — получаешь битый байт. В production это вылезает при обрезании логов с кириллицей или эмодзи. Решение: не используй slice на Uint8Array вслепую, применяй subarray и TextDecoder с stream: true для восстановления границ.Грабли 2. Потоковое декодирование без stream: true
Web Streams отдают данные чанками. Если декодировать каждый чанк как отдельную строку, последний может быть урезан — многобайтовый символ разобьется на два чанка. Типичная ошибка:
// Ломается на границе чанков
for await (const chunk of reader) {
const str = new TextDecoder().decode(chunk);
}
Нужно так:
const decoder = new TextDecoder('utf-8', { stream: true });
for await (const chunk of reader) {
const str = decoder.decode(chunk, { stream: true });
}
const final = decoder.decode(); // завершающий вызовПропустишь
stream: true — получишь битые данные на границах чанков, например, при парсинге CSV с кириллицей в production.Грабли 3. Утечка памяти из-за создания инстансов
Создание
new TextEncoder() внутри цикла или хендлера запросов вызывает GC-шторм. Вынеси в константы модуля один раз. Тоже самое с TextDecoder — кэшируй для всех чанков потока.Практические советы при портировании
-
Buffer.concat заменяй на ручное объединение через new Uint8Array(totalLength) и set.-
fs.createReadStream заменяй на fetch + response.body (ReadableStream).- Для энкодинга/декодинга используй только
TextEncoder/TextDecoder с stream: true на чанках.- Никогда не доверяй прямому
slice по Uint8Array, если внутри UTF-8 — это ломает строки с акцентами, кириллицей и эмодзи в production.Вывод: Грабли не в том, что Buffer нет в браузере, а в том, что TextEncoder.encode ведет себя иначе — учитывай многобайтовые символы, кэшируй инстансы и всегда декодируй с stream: true.