Skip to content

JavaScript二进制数据处理

前言

在前端开发中,二进制数据处理是一个重要的知识点。无论是文件上传下载、图片处理、音视频处理还是WebSocket通信,都需要对二进制数据进行操作。本文将系统地介绍JavaScript中的二进制数据处理方案。

一、基础概念

1.1 位、字节、字

在深入二进制操作之前,我们需要理解几个基本概念:

  • 位(bit): 最小的存储单位,存储0或1
  • 字节(byte): 8个位组成一个字节
  • 字(word):
    • 32位系统: 1字=32位=4字节
    • 64位系统: 1字=64位=8字节

1.2 字符集与编码

字符集

字符集定义了字符和二进制的映射关系,常见的有:

  • ASCII: 最早的字符集,使用7位二进制表示128个字符
  • Unicode: 统一的字符集,使用16位表示字符
  • GB2312/GBK: 中文字符集

编码方式

  • UTF-8: 可变长度编码,英文1字节,中文3字节
  • UTF-16: 统一使用2或4字节编码
  • UTF-32: 统一使用4字节编码

二、ArrayBuffer

2.1 基本概念

ArrayBuffer是最基础的二进制对象,表示固定长度的原始二进制数据。

javascript
// 创建16字节的缓冲区
const buffer = new ArrayBuffer(16);

// 获取字节长度
console.log(buffer.byteLength); // 16

// 切片操作
const newBuffer = buffer.slice(0, 8);

2.2 特点

  1. 不能直接读写
  2. 固定长度不可修改
  3. 需要通过视图(TypedArray/DataView)操作

三、TypedArray

3.1 类型概览

类型字节数取值范围用途
Int8Array1-128 到 127有符号8位整数
Uint8Array10 到 255无符号8位整数
Uint8ClampedArray10 到 255图像处理
Int16Array2-32768 到 32767有符号16位整数
Uint16Array20 到 65535无符号16位整数
Int32Array4-2^31 到 2^31-1有符号32位整数
Uint32Array40 到 2^32-1无符号32位整数
Float32Array41.2×10^-38 到 3.4×10^3832位浮点数
Float64Array85.0×10^-324 到 1.8×10^30864位浮点数

3.2 创建方式

javascript
// 1. 通过ArrayBuffer创建
const buffer = new ArrayBuffer(16);
const int8Array = new Int8Array(buffer);

// 2. 直接创建
const uint8Array = new Uint8Array(16);

// 3. 通过数组创建
const int32Array = new Int32Array([1, 2, 3, 4]);

// 4. 从其他TypedArray创建
const int16Array = new Int16Array(int32Array);

3.3 常用操作

javascript
const array = new Int32Array(4);

// 写入数据
array[0] = 100;
array.set([1, 2], 1); // 从索引1开始设置[1,2]

// 遍历
array.forEach(x => console.log(x));

// 转换
const normalArray = Array.from(array);
const newTypedArray = array.subarray(1, 3);

// 判断
console.log(ArrayBuffer.isView(array)); // true
console.log(array.buffer instanceof ArrayBuffer); // true

四、DataView

4.1 基本使用

javascript
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);

// 写入数据
view.setInt8(0, 42);
view.setInt16(1, 12345, true); // 第三个参数表示是否使用小端字节序
view.setFloat64(3, 123.456);

// 读取数据
console.log(view.getInt8(0));
console.log(view.getInt16(1, true));
console.log(view.getFloat64(3));

4.2 字节序

javascript
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);

// 写入数据 0x12345678
view.setInt32(0, 0x12345678);

// 大端字节序读取 (默认)
console.log(view.getInt32(0).toString(16)); // 12345678

// 小端字节序读取
console.log(view.getInt32(0, true).toString(16)); // 78563412

五、Blob与File

5.1 Blob对象

javascript
// 创建Blob
const blob1 = new Blob(['Hello World'], { type: 'text/plain' });
const blob2 = new Blob([JSON.stringify({name: 'test'})], { type: 'application/json' });

// Blob操作
async function handleBlob(blob) {
    // 获取文本
    const text = await blob.text();
    
    // 获取ArrayBuffer
    const buffer = await blob.arrayBuffer();
    
    // 切片
    const slice = blob.slice(0, 5, 'text/plain');
    
    // 创建URL
    const url = URL.createObjectURL(blob);
    // 使用完记得释放
    URL.revokeObjectURL(url);
}

5.2 File对象

javascript
// 创建File
const file = new File(['Hello World'], 'test.txt', { 
    type: 'text/plain',
    lastModified: Date.now()
});

// File属性
console.log(file.name);         // 文件名
console.log(file.size);         // 文件大小
console.log(file.type);         // MIME类型
console.log(file.lastModified); // 最后修改时间

// 文件上传示例
async function uploadFile(file) {
    const formData = new FormData();
    formData.append('file', file);
    
    const response = await fetch('/upload', {
        method: 'POST',
        body: formData
    });
    
    return response.json();
}

六、Base64

6.1 基本使用

javascript
// 字符串转Base64
const str = 'Hello World';
const base64 = btoa(str);
console.log(base64); // SGVsbG8gV29ybGQ=

// Base64转字符串
const decoded = atob(base64);
console.log(decoded); // Hello World

// 处理Unicode字符
const unicode = '你好';
const base64Unicode = btoa(encodeURIComponent(unicode));
const decodedUnicode = decodeURIComponent(atob(base64Unicode));

6.2 实用工具函数

javascript
// Blob转Base64
function blobToBase64(blob) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = reject;
        reader.readAsDataURL(blob);
    });
}

// Base64转Blob
function base64ToBlob(base64, type = 'application/octet-stream') {
    const [header, data] = base64.split(',');
    const bytes = atob(data);
    const array = new Uint8Array(bytes.length);
    
    for (let i = 0; i < bytes.length; i++) {
        array[i] = bytes.charCodeAt(i);
    }
    
    return new Blob([array], { type });
}

// 图片转Base64
function imageToBase64(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = 'Anonymous';
        img.onload = () => {
            const canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            
            resolve(canvas.toDataURL('image/png'));
        };
        img.onerror = reject;
        img.src = url;
    });
}

七、FileReader

7.1 基本用法

javascript
const reader = new FileReader();

// 事件处理
reader.onload = (e) => console.log(e.target.result);
reader.onerror = (e) => console.error('Error:', e);
reader.onprogress = (e) => {
    if (e.lengthComputable) {
        const progress = (e.loaded / e.total) * 100;
        console.log(`Progress: ${progress}%`);
    }
};

// 读取方法
reader.readAsText(file);        // 读取为文本
reader.readAsArrayBuffer(file); // 读取为ArrayBuffer
reader.readAsDataURL(file);     // 读取为Base64
reader.readAsBinaryString(file);// 读取为二进制字符串

7.2 Promise封装

javascript
function readFile(file, readAs = 'text') {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        
        reader.onload = () => resolve(reader.result);
        reader.onerror = () => reject(reader.error);
        
        switch(readAs) {
            case 'text':
                reader.readAsText(file);
                break;
            case 'arrayBuffer':
                reader.readAsArrayBuffer(file);
                break;
            case 'dataURL':
                reader.readAsDataURL(file);
                break;
            case 'binaryString':
                reader.readAsBinaryString(file);
                break;
            default:
                reject(new Error('Unsupported read type'));
        }
    });
}

// 使用示例
async function handleFile(file) {
    try {
        const text = await readFile(file, 'text');
        const arrayBuffer = await readFile(file, 'arrayBuffer');
        const dataURL = await readFile(file, 'dataURL');
        
        console.log({ text, arrayBuffer, dataURL });
    } catch (error) {
        console.error('Error reading file:', error);
    }
}

八、实际应用示例

8.1 文件下载

javascript
async function downloadFile(url, filename) {
    try {
        const response = await fetch(url);
        const blob = await response.blob();
        const objectUrl = URL.createObjectURL(blob);
        
        const a = document.createElement('a');
        a.href = objectUrl;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        
        // 清理
        document.body.removeChild(a);
        URL.revokeObjectURL(objectUrl);
    } catch (error) {
        console.error('Download failed:', error);
    }
}

8.2 大文件分片上传

javascript
async function uploadLargeFile(file, chunkSize = 1024 * 1024) {
    const chunks = Math.ceil(file.size / chunkSize);
    const uploads = [];
    
    for (let i = 0; i < chunks; i++) {
        const start = i * chunkSize;
        const end = Math.min(start + chunkSize, file.size);
        const chunk = file.slice(start, end);
        
        const formData = new FormData();
        formData.append('chunk', chunk);
        formData.append('index', i);
        formData.append('total', chunks);
        
        uploads.push(
            fetch('/upload/chunk', {
                method: 'POST',
                body: formData
            })
        );
    }
    
    await Promise.all(uploads);
    
    // 通知服务器合并文件
    await fetch('/upload/complete', {
        method: 'POST',
        body: JSON.stringify({ filename: file.name })
    });
}

九、最佳实践

  1. 选择合适的数据结构

    • 简单数值操作用TypedArray
    • 复杂二进制操作用DataView
    • 文件操作用Blob/File
  2. 性能优化

    • 避免不必要的数据转换
    • 大文件操作考虑分片处理
    • 及时释放不需要的资源
  3. 内存管理

    • 使用完URL.createObjectURL后调用revokeObjectURL
    • 大文件处理完后清除引用
    • 避免内存泄漏
  4. 错误处理

    • 总是处理异步操作的错误
    • 验证文件类型和大小
    • 考虑网络状况

总结

JavaScript提供了丰富的二进制数据处理能力,掌握这些API对于开发高质量的Web应用至关重要。本文详细介绍了从基础概念到实际应用的各个方面,希望能帮助你更好地理解和使用JavaScript的二进制数据处理能力。