Base64 w przeglądarce i Node.js: kontrakt jednobajtowy, dwa interfejsy API

Użyj TextEncoder w przeglądarkach i Buffer w Node.js, a następnie przetestuj ich dane wyjściowe na tych samych urządzeniach Unicode i binarnych.

Klient przeglądarki i usługa Node.js powinny generować ten sam Base64 dla tych samych bajtów. Nie muszą do tego używać tego samego API. Ważną umową jest bajtowa reprezentacja tekstu, alfabetu oraz traktowanie dopełnienia i nieprawidłowych danych wejściowych.

Przeglądarka: wyraźnie zaznacz krok UTF-8

Tradycyjne funkcje przeglądarki działają na ciągach bajtów. Konwertuj tekst Unicode za pomocą TextEncoder, a następnie zakoduj te bajty. W ścieżce powrotnej zrekonstruuj tablicę bajtów i jawnie zdekoduj UTF-8. W tym przykładzie zastosowano pętlę, aby uniknąć rozszerzania dużej tablicy na argumenty funkcji, chociaż koder pliku produkcyjnego powinien nadal używać ograniczonych fragmentów lub odpowiedniego bajtu API.

const text = '中文😊';
const bytes = new TextEncoder().encode(text);
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
const encoded = btoa(binary);
console.log(encoded); // 5Lit5paH8J+Yig==
const decodedBytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
const decoded = new TextDecoder('utf-8', { fatal: true }).decode(decodedBytes);
console.log(decoded); // 中文😊

Node.js: zachowaj Buffer na granicy bajtów

Buffer.from(text, 'utf8') sprawia, że kodowanie tekstu staje się widoczne. Dzwonię toString('base64') następnie reprezentuje te bajty. Dekodowanie do Buffer zachowuje dane binarne; natychmiastowa konwersja na ciąg UTF-8 jest odpowiednia tylko wtedy, gdy wiadomo, że ładunek jest tekstem.

import { Buffer } from 'node:buffer';
const encoded = Buffer.from('中文😊', 'utf8').toString('base64');
console.log(encoded); // 5Lit5paH8J+Yig==
const bytes = Buffer.from(encoded, 'base64');
console.log(new TextDecoder('utf-8', { fatal: true }).decode(bytes)); // 中文😊

Fatalny TextDecoder w tym przykładzie jest zamierzony. Zwykła konwersja ciągów Buffer UTF-8 może wstawić znaki zastępcze w przypadku nieprawidłowych danych wejściowych. Pomyślne wyświetlenie tekstu ukryłoby wówczas, że oryginalne bajty nie mogły zostać wiernie odtworzone.

Uzgodnij osobno walidację

Dekoder Base64 węzła akceptuje niektóre formularze wejściowe, które bardziej rygorystyczna aplikacja może odrzucić. Nie używaj udanej konwersji Buffer jako dowodu, że dostarczono kanoniczną reprezentację protokołu. Jeśli dokładna składnia ma znaczenie dla podpisów, kluczy pamięci podręcznej lub serializacji interoperacyjnej, sprawdź ją przed dekodowaniem.

W przypadku wspólnego testu regresji uwzględnij ASCII, język chiński, emoji, bajt zerowy, bajt FF i wszystkie długości wokół granicy trzech bajtów. Porównaj bajty w różnych środowiskach wykonawczych. Przetestuj także bezpieczne wyjście URL z urządzeniem, które faktycznie zawiera podstawione znaki; dane wejściowe, których wyjściem są tylko litery, nie mogą udowodnić, że opcja wariantu działa.

Podczas czytania plików w węźle zachowaj zwrócony Buffer zamiast stosować kodowanie tekstu do odczytanego pliku. W przeglądarce użyj File.arrayBuffer(). Te małe wybory graniczne pozwalają uniknąć większości przypadkowych uszkodzeń. Zobacz Node.js Buffer dla zachowania węzła i MDN: btoa() dla umowy na ciąg bajtów przeglądarki.