1. File 和 Blob 对象
在JavaScript中,处理文件和二进制数据的API主要依赖于File
、FileReader
、Blob
和ArrayBuffer
等对象。
File 对象是代表用户选择的文件,它是 Blob
对象的一个扩展,具有一些额外的属性,比如name
、lastModified
等。Blob
对象则代表了一段不可变的原始数据,通常用于存储二进制数据。
html
<input type="file" name="file" id="file">
<script>
// 创建一个 File 对象示例
const fileDom = document.getElementById('file');
fileDom.onchange = function(event) {
const file = event.target.files[0]; // 获取第一个文件
console.log(file); // 文件对象
console.log(file.name); // 文件名
console.log(file.size); // 文件大小
};
</script>
上传一个文件得到如下结果
2. FileReader API
FileReader
是一种用于异步读取文件内容的API,可以读取File
和Blob
对象的内容,支持多种格式,包括文本和二进制。
常用方法:
readAsText(file)
: 以文本形式读取文件。readAsDataURL(file)
: 读取文件并返回一个以data URL
形式表示的结果。readAsArrayBuffer(file)
: 以ArrayBuffer
格式读取文件。
示例:
js
// 基础使用
const reader = new FileReader();
reader.onload = function(event) {
const text = event.target.result; // 读取到的文件内容
console.log(text);
};
reader.readAsText(file); // 假设 file 是一个 File 对象
html
<input type="file" name="file" id="file">
<script>
// 创建一个 File 对象示例
const fileDom = document.getElementById('file');
fileDom.onchange = function(event) {
const file = event.target.files[0]; // 获取第一个文件
console.log(file); // 文件对象
const reader = new FileReader();
reader.onload = function(event) {
const text = event.target.result; // 读取到的文件内容
console.log(text);
};
reader.readAsText(file); // file 是一个 File 对象
console.log(file.name); // 文件名
console.log(file.size); // 文件大小
};
</script>
得到的效果如图
3. Blob 对象
Blob
对象的主要作用是处理二进制数据,通常用于文件上传或生成本地文件。
js
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const url = URL.createObjectURL(blob); // 创建一个 Blob URL
const a = document.createElement('a');
a.href = url;
a.download = 'hello.txt'; // 设置下载文件名
a.click(); // 触发下载
4. ArrayBuffer 和 TypedArray
ArrayBuffer
是一种通用的、固定大小的原始二进制数据缓冲区。与之配合使用的是类型数组(TypedArray
),如 Uint8Array
、Float32Array
等,它们允许在缓冲区中以特定类型的方式读取和操作数据。
js
const buffer = new ArrayBuffer(16); // 创建一个 16 字节的 ArrayBuffer
const uint8View = new Uint8Array(buffer); // 创建一个视图
uint8View[0] = 255; // 操作视图中的数据
console.log(uint8View[0]); // 输出 255
5. 处理二进制数据
通过 FileReader
和 ArrayBuffer
,我们可以读取并处理文件中的二进制数据。例如,读取一个图像文件并显示:
js
const fileDom = document.getElementById('file');
fileDom.onchange = function(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = function(e) {
const arrayBuffer = e.target.result; // 获取 ArrayBuffer
const imgBlob = new Blob([arrayBuffer], { type: file.type }); // 创建 Blob
const imgUrl = URL.createObjectURL(imgBlob); // 创建 URL
const imgElement = document.createElement('img');
imgElement.src = imgUrl; // 设置图像源
document.body.appendChild(imgElement); // 添加到文档
};
reader.readAsArrayBuffer(file); // 读取为 ArrayBuffer
};
这样就读取出来了内容,效果如下
6. File 与 Blob 之间的转换 (常见)
File 是 Blob 的子类型,因此可以直接将一个 File 对象视为 Blob,实际上它们是可以互换的。
js
const fileDom = document.getElementById('file');
fileDom.onchange = function(event) {
const file = event.target.files[0]; // 获取 File 对象
const blob = new Blob([file]); // 将 File 转换为 Blob
console.log(blob);
};
7. Blob 与 ArrayBuffer 之间的转换 (常见)
要将 Blob 转换为 ArrayBuffer,首先需要使用 FileReader 读取 Blob 内容:
js
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const reader = new FileReader();
reader.onload = function(event) {
const arrayBuffer = event.target.result; // 读取结果为 ArrayBuffer
console.log(arrayBuffer);
};
reader.readAsArrayBuffer(blob); // 将 Blob 读取为 ArrayBuffer
要将 ArrayBuffer 转换为 Blob:
js
const arrayBuffer = new Uint8Array([72, 101, 108, 108, 111]); // 创建 ArrayBuffer
const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' }); // 转换为 Blob
8. Blob 与 字符串之间的转换
要将 Blob 转换为字符串,使用 FileReader 的 readAsText
方法:
js
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const reader = new FileReader();
reader.onload = function(event) {
const text = event.target.result; // 转换为字符串
console.log(text);
};
reader.readAsText(blob); // 读取 Blob 为字符串
要将字符串转换为 Blob:
js
const text = "Hello, World!";
const blob = new Blob([text], { type: 'text/plain' }); // 将字符串转换为 Blob
9. ArrayBuffer 与字符串之间的转换
要将 ArrayBuffer 转换为字符串,可以使用 TextDecoder
:
js
const arrayBuffer = new Uint8Array([72, 101, 108, 108, 111]).buffer; // 创建 ArrayBuffer
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(arrayBuffer); // 转换为字符串
console.log(text); // 输出 "Hello"
要将字符串转换为 ArrayBuffer,可以使用 TextEncoder
:
js
const text = "Hello, World!";
const encoder = new TextEncoder();
const arrayBuffer = encoder.encode(text).buffer; // 转换为 ArrayBuffer
10. DataView
DataView
是一个用于在ArrayBuffer
上读取和写入数据的视图。它可以用来以不同的格式(比如整数
、浮点数
等)访问二进制数据,而不需要考虑底层数据结构的复杂性。
特性和用法
- 灵活性 :
DataView
可以在一个ArrayBuffer
中指定不同的数据格式。 - 字节序:可以指定读写操作的字节序(大端和小端)。
创建 DataView
js
const buffer = new ArrayBuffer(16); // 创建一个16字节的ArrayBuffer
const dataView = new DataView(buffer); // 创建 DataView 视图
// 写入数据
dataView.setUint8(0, 255); // 在索引0位置写入一个8位无符号整数
dataView.setFloat32(1, 3.14); // 在索引1位置写入一个32位浮点数
// 读取数据
const firstValue = dataView.getUint8(0); // 读取8位无符号整数
const secondValue = dataView.getFloat32(1); // 读取32位浮点数
console.log(firstValue); // 255
console.log(secondValue); // 3.14
11. Buffer
Buffer
是 Node.js 中用于处理二进制数据的类。它不是JavaScript标准的一部分,但在Node.js环境中广泛使用。Buffer
用于操作原始数据,尤其是在文件操作和网络请求中。
特性和用法
- 低级数据操作:可以直接处理二进制数据,尤其适合文件读取、写入和网络通信。
- 与字符串的转换:可以直接与字符串进行转换,指定编码格式。
创建 Buffer
js
const buffer = Buffer.from('Hello, World!', 'utf-8'); // 从字符串创建 Buffer
console.log(buffer); // 打印 Buffer 对象
const byteArray = new Uint8Array(buffer); // 将 Buffer 转换为 Uint8Array
console.log(byteArray); // 输出字节数组
// 写入 Buffer 示例
const buf = Buffer.alloc(16); // 分配16字节的Buffer
buf.write('Hi'); // 写入字符串
console.log(buf.toString()); // 输出 Hi,未写入的部分为默认值(0)
DataView 与 Buffer 的比较
特性 | DataView | Buffer |
---|---|---|
环境 | Web,浏览器 | Node.js |
用途 | 处理 ArrayBuffer 中的数据 |
处理二进制数据,尤其是 I/O 操作 |
数据类型访问 | 支持各种数据类型的读取和写入 | 特别适合字符串和二进制数据的处理 |
编码支持 | 无需编码,只依靠数据视图操作 | 支持多种字符编码(如 UTF-8, ASCII 等) |
总结
- DataView 是用于在
ArrayBuffer
上以不同格式读写数据的工具,适合在浏览器环境中处理原始二进制数据。 - Buffer 是 Node.js 中处理二进制数据的类,适合文件和网络操作,提供了更丰富的字符串和二进制数据的交互功能。
12. URL API
- URL.revokeObjectURL(url) : 释放由
URL.createObjectURL
创建的 URL,帮助减少内存消耗。(URL.createObjectURL
是一个用于创建指向Blob
或File
对象的 URL 的方法) - URL.createObjectURL() : 前面提到的,根据 Blob 或 File 创建可用于
<img>
、<a>
等元素的 URL。
URL.revokeObjectURL(url) 使用场景
- 显示用户上传的图像。
- 生成可下载文件的链接。
- 在
<video>
标签中加载视频文件。
URL.revokeObjectURL(url) 使用场景
创建可下载的链接
创建的对象 URL 在不再需要时应调用 URL.revokeObjectURL(url)
进行释放,避免内存泄漏。这对于长时间存在的 URL 尤其重要。
js
<button id="downloadBtn">下载文件</button>
<script>
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const url = URL.createObjectURL(blob); // 创建Blob URL
document.getElementById('downloadBtn').addEventListener('click', function() {
const a = document.createElement('a'); // 创建一个链接元素
a.href = url; // 设置链接地址
a.download = 'hello.txt'; // 设置下载文件名
a.click(); // 触发下载
URL.revokeObjectURL(url); // 释放对象 URL
});
</script>
表单问题处理
当使用 FormData
对象发送数据时,HTTP 请求的 Content-Type
头会被自动设置为 multipart/form-data
。这是因为在 multipart/form-data
编码方式下,每个表单字段都会被编码为一个单独的部分,每个部分都有自己的头部信息,包括字段名和内容类型。
js
// file文件一般都是以 FormData 传给后端的
const formData = new FormData();
formData.append('file', file);
HTTP 请求头
在上述示例中,fetch
请求会自动设置 Content-Type
头为 multipart/form-data
,并且会包含一个边界字符串(boundary string)来分隔不同的部分。例如:
js
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
解释
- multipart/form-data: 这种编码方式主要用于文件上传,因为它可以处理二进制数据。
- boundary: 边界字符串是一个唯一的字符串,用于分隔不同的表单字段。浏览器会自动生成这个字符串。
手动设置 Content-Type
虽然通常不需要手动设置 Content-Type
,因为 fetch
(Axios
也是一样的) 会自动处理,如果你需要手动设置Content-Type
也是可以的,但是要注意:手动设置Content-Type
时边界字符串不会自动添加,这会导致服务器无法正确解析请求。因此,通常情况下不建议手动设置 Content-Type
。
乱码流的概念
"乱码流"并不是一个标准的技术术语,但在实际开发中,它通常指的是由于编码问题导致的数据在传输或显示过程中出现的乱码现象。这种现象通常发生在以下几个场景中:
- 字符编码不匹配: 发送方和接收方使用的字符编码不一致。
- 数据损坏: 数据在传输过程中被损坏或丢失。
- 格式错误: 数据格式不符合预期,导致解析错误。
乱码流的数据类型
乱码流本质上仍然是二进制数据或文本数据,具体类型取决于原始数据的格式。常见的数据类型包括:
- 二进制数据 (
ArrayBuffer
,Uint8Array
,Blob
): 通常用于文件传输、图像数据等。 - 文本数据 (
String
): 通常用于表单数据、JSON 等。
处理乱码流的方法
1. 字符编码不匹配
确保发送方和接收方使用相同的字符编码。常见的字符编码包括 UTF-8
, ISO-8859-1
, GBK
等。
示例:设置请求和响应的字符编码
javascript
// 使用 Axios 设置请求的字符编码
axios.post('/submit-form', formData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
服务器端设置响应的字符编码
javascript
// Node.js Express 示例
app.get('/data', (req, res) => {
res.set('Content-Type', 'text/html; charset=UTF-8');
res.send('<h1>Hello, World!</h1>');
});
2. 数据损坏
确保数据在传输过程中没有被损坏。可以使用校验和(如 MD5、SHA-1)来验证数据的完整性。
示例:使用 MD5 校验
javascript
const crypto = require('crypto');
function calculateMD5(data) {
return crypto.createHash('md5').update(data).digest('hex');
}
// 发送方
const data = 'Hello, World!';
const md5 = calculateMD5(data);
axios.post('/submit-form', { data, md5 })
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
// 接收方
app.post('/submit-form', (req, res) => {
const { data, md5 } = req.body;
const calculatedMd5 = calculateMD5(data);
if (calculatedMd5 === md5) {
res.send('Data is valid');
} else {
res.status(400).send('Data is corrupted');
}
});
3. 格式错误
确保数据格式符合预期。对于 JSON 数据,可以使用 JSON.parse
和 JSON.stringify
来处理。
示例:处理 JSON 数据
javascript
// 发送方
const data = { username: 'john_doe', email: 'john@example.com' };
axios.post('/submit-form', JSON.stringify(data), {
headers: {
'Content-Type': 'application/json; charset=UTF-8'
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
// 接收方
app.post('/submit-form', (req, res) => {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
const data = JSON.parse(body);
res.send(`Received data: ${JSON.stringify(data)}`);
} catch (error) {
res.status(400).send('Invalid JSON format');
}
});
});
总结
- 乱码流通常是由于字符编码不匹配、数据损坏或格式错误导致的。
- 处理方法包括确保字符编码一致、使用校验和验证数据完整性和确保数据格式正确。
- 数据类型可以是二进制数据或文本数据,具体取决于原始数据的格式。