Appearance
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 特点
- 不能直接读写
- 固定长度不可修改
- 需要通过视图(TypedArray/DataView)操作
三、TypedArray
3.1 类型概览
类型 | 字节数 | 取值范围 | 用途 |
---|---|---|---|
Int8Array | 1 | -128 到 127 | 有符号8位整数 |
Uint8Array | 1 | 0 到 255 | 无符号8位整数 |
Uint8ClampedArray | 1 | 0 到 255 | 图像处理 |
Int16Array | 2 | -32768 到 32767 | 有符号16位整数 |
Uint16Array | 2 | 0 到 65535 | 无符号16位整数 |
Int32Array | 4 | -2^31 到 2^31-1 | 有符号32位整数 |
Uint32Array | 4 | 0 到 2^32-1 | 无符号32位整数 |
Float32Array | 4 | 1.2×10^-38 到 3.4×10^38 | 32位浮点数 |
Float64Array | 8 | 5.0×10^-324 到 1.8×10^308 | 64位浮点数 |
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 })
});
}
九、最佳实践
选择合适的数据结构
- 简单数值操作用TypedArray
- 复杂二进制操作用DataView
- 文件操作用Blob/File
性能优化
- 避免不必要的数据转换
- 大文件操作考虑分片处理
- 及时释放不需要的资源
内存管理
- 使用完URL.createObjectURL后调用revokeObjectURL
- 大文件处理完后清除引用
- 避免内存泄漏
错误处理
- 总是处理异步操作的错误
- 验证文件类型和大小
- 考虑网络状况
总结
JavaScript提供了丰富的二进制数据处理能力,掌握这些API对于开发高质量的Web应用至关重要。本文详细介绍了从基础概念到实际应用的各个方面,希望能帮助你更好地理解和使用JavaScript的二进制数据处理能力。