ByteString is a specialized class for handling binary data (bytes fields) in Protocol Buffer messages. It provides safe, immutable handling of byte arrays with conversion utilities between different formats.
const { ByteString } = require('google-protobuf');
// Create from base64 string
const fromB64 = ByteString.fromBase64("SGVsbG8gV29ybGQ=");
// Create from Uint8Array or array of numbers
const fromBytes = ByteString.fromUint8Array(new Uint8Array([72, 101, 108, 108, 111]));
// Create empty ByteString
const empty = ByteString.empty();
// Check if empty
const isEmpty = byteString.isEmpty();
// Get length
const length = byteString.getLength();Type Definitions:
/**
* @constructor
* Immutable wrapper for bytes fields
*/
/**
* @param {string} value Base64 encoded string (RFC 4648 section 4)
* @return {!ByteString}
*/
/**
* @param {!Uint8Array|!Array<number>} value Byte array data
* @return {!ByteString}
*/const { ByteString } = require('google-protobuf');
// Create from base64
const bs = ByteString.fromBase64("SGVsbG8gV29ybGQ=");
// Convert to base64
const base64String = bs.toBase64();
console.log(base64String); // "SGVsbG8gV29ybGQ="
// URL-safe base64 (alternative encoding)
const urlSafeB64 = bs.toBase64(true); // Uses URL-safe alphabet// Create from Uint8Array
const bytes = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);
const bs = ByteString.fromUint8Array(bytes);
// Convert to Uint8Array
const restored = bs.toUint8Array();
console.log(restored); // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])
// Create from regular array of numbers
const numberArray = [65, 66, 67];
const bsFromArray = ByteString.fromUint8Array(numberArray);const { ByteString } = require('google-protobuf');
// Create from UTF-8 string
const message = "Hello, 世界!";
const utf8Bytes = new TextEncoder().encode(message);
const bs = ByteString.fromUint8Array(utf8Bytes);
// Convert back to string (requires manual UTF-8 decoding)
const restoredBytes = bs.toUint8Array();
const restoredMessage = new TextDecoder().decode(restoredBytes);
console.log(restoredMessage); // "Hello, 世界!"
// Work with binary strings
const binaryData = "\x48\x65\x6C\x6C\x6F"; // "Hello" as binary string
const binaryBytes = new Uint8Array([...binaryData].map(c => c.charCodeAt(0)));
const bsFromBinary = ByteString.fromUint8Array(binaryBytes);const { ByteString } = require('google-protobuf');
const bs1 = ByteString.fromBase64("SGVsbG8="); // "Hello"
const bs2 = ByteString.fromUint8Array([72, 101, 108, 108, 111]); // Same data
const bs3 = ByteString.fromBase64("V29ybGQ="); // "World"
// ByteString comparison
const areEqual = bs1.equals(bs2);
console.log(areEqual); // true
const areDifferent = bs1.equals(bs3);
console.log(areDifferent); // false
// Compare with null/undefined
const nullComparison = bs1.equals(null);
console.log(nullComparison); // false
// Hash code for use in maps/sets
const hash1 = bs1.hashCode();
const hash2 = bs2.hashCode();
console.log(hash1 === hash2); // true (same data = same hash)Type Definitions:
/**
* @param {?ByteString} other ByteString to compare with
* @return {boolean} Whether the ByteStrings contain identical data
*/
/**
* @return {number} Hash code for this ByteString
*/const { ByteString } = require('google-protobuf');
// Example: Working with bytes fields in generated messages
class DocumentMessage extends Message {
getContent() { return this.content_; }
setContent(value) { this.content_ = value; }
getThumbnail() { return this.thumbnail_; }
setThumbnail(value) { this.thumbnail_ = value; }
}
// Set bytes field using ByteString
const document = new DocumentMessage();
// From file data
const fileData = new Uint8Array([/* file bytes */]);
const contentBytes = ByteString.fromUint8Array(fileData);
document.setContent(contentBytes);
// From base64 encoded thumbnail
const thumbnailB64 = "iVBORw0KGgoAAAANSUhEUgAA..."; // Base64 image data
const thumbnailBytes = ByteString.fromBase64(thumbnailB64);
document.setThumbnail(thumbnailBytes);
// Serialize message (ByteString handles conversion automatically)
const serialized = document.serializeBinary();
// Deserialize and access bytes
const restored = DocumentMessage.deserializeBinary(serialized);
const restoredContent = restored.getContent(); // Returns ByteString
const contentAsBytes = restoredContent.toUint8Array();const { BinaryReader, BinaryWriter, ByteString } = require('google-protobuf');
// Writing ByteString with BinaryWriter
const writer = new BinaryWriter();
const data = ByteString.fromBase64("SGVsbG8=");
writer.writeBytes(1, data); // ByteString automatically handled
const result = writer.getResultBuffer();
// Reading ByteString with BinaryReader
const reader = new BinaryReader(result);
while (reader.nextField()) {
if (reader.getFieldNumber() === 1) {
const readBytes = reader.readBytes(); // Returns Uint8Array by default
const asByteString = ByteString.fromUint8Array(readBytes);
console.log(asByteString.toBase64()); // "SGVsbG8="
}
}const { ByteString } = require('google-protobuf');
// ByteString is immutable - safe to share references
const originalData = ByteString.fromBase64("SGVsbG8=");
const sharedRef1 = originalData;
const sharedRef2 = originalData;
// No risk of accidental mutation
console.log(sharedRef1 === sharedRef2); // true - same object reference
console.log(sharedRef1.equals(sharedRef2)); // true - same data
// Operations create new instances
const base64String = originalData.toBase64(); // Safe conversion
const byteArray = originalData.toUint8Array(); // Creates new Uint8Array// Efficient for repeated operations
const cache = new Map();
function getOrCreateByteString(base64Data) {
if (!cache.has(base64Data)) {
cache.set(base64Data, ByteString.fromBase64(base64Data));
}
return cache.get(base64Data);
}
// Reuse ByteString instances for identical data
const bs1 = getOrCreateByteString("SGVsbG8=");
const bs2 = getOrCreateByteString("SGVsbG8=");
console.log(bs1 === bs2); // true - same cached instanceconst { ByteString } = require('google-protobuf');
// Efficient: Create once, use multiple times
const data = ByteString.fromUint8Array(largeByteArray);
const base64 = data.toBase64();
const restored = data.toUint8Array();
// Less efficient: Multiple conversions
// Don't do this in performance-critical code:
function inefficientUsage(base64Data) {
return ByteString.fromBase64(base64Data)
.toUint8Array()
.map(b => b.toString(16))
.join('');
}
// Better: Convert once, work with result
function efficientUsage(base64Data) {
const bs = ByteString.fromBase64(base64Data);
const bytes = bs.toUint8Array();
return bytes.map(b => b.toString(16)).join('');
}const { ByteString } = require('google-protobuf');
// Base64 validation
try {
const invalid = ByteString.fromBase64("Invalid!Base64@"); // May throw
} catch (error) {
console.error("Invalid base64:", error.message);
}
// Safe creation with validation
function createSafeByteString(base64Input) {
try {
return ByteString.fromBase64(base64Input);
} catch (error) {
console.warn("Invalid base64 input, creating empty ByteString");
return ByteString.empty();
}
}
// Array input validation
const validBytes = [0, 255, 128]; // Valid byte values
const invalidBytes = [256, -1, 300]; // Will be truncated to valid range
const bs1 = ByteString.fromUint8Array(validBytes);
const bs2 = ByteString.fromUint8Array(invalidBytes); // Values converted to valid bytes// Safe handling of null/undefined
function safeByteStringOps(maybeByteString) {
if (!maybeByteString) {
return ByteString.empty();
}
if (maybeByteString instanceof ByteString) {
return maybeByteString;
}
// Handle string input
if (typeof maybeByteString === 'string') {
try {
return ByteString.fromBase64(maybeByteString);
} catch {
return ByteString.empty();
}
}
// Handle array input
if (Array.isArray(maybeByteString) || maybeByteString instanceof Uint8Array) {
return ByteString.fromUint8Array(maybeByteString);
}
return ByteString.empty();
}const { ByteString } = require('google-protobuf');
// File upload: Convert File to ByteString
async function fileToByteString(file) {
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
return ByteString.fromUint8Array(uint8Array);
}
// File download: Convert ByteString to downloadable blob
function byteStringToBlob(byteString, mimeType = 'application/octet-stream') {
const bytes = byteString.toUint8Array();
return new Blob([bytes], { type: mimeType });
}
// Usage example
async function handleFileUpload(fileInput) {
const file = fileInput.files[0];
const byteString = await fileToByteString(file);
// Store in Protocol Buffer message
const message = new FileMessage();
message.setContent(byteString);
message.setFileName(file.name);
message.setMimeType(file.type);
return message;
}// Working with image data
async function processImage(imageByteString) {
// Convert to blob for canvas processing
const blob = new Blob([imageByteString.toUint8Array()], { type: 'image/jpeg' });
const imageUrl = URL.createObjectURL(blob);
// Load into canvas
const img = new Image();
img.src = imageUrl;
await new Promise(resolve => img.onload = resolve);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Process and return as ByteString
const processedBlob = await new Promise(resolve => canvas.toBlob(resolve));
const processedBytes = new Uint8Array(await processedBlob.arrayBuffer());
URL.revokeObjectURL(imageUrl);
return ByteString.fromUint8Array(processedBytes);
}