深度剖析File、Blob以及二进制文件相关api

1. File 和 Blob 对象

在JavaScript中,处理文件和二进制数据的API主要依赖于FileFileReaderBlobArrayBuffer等对象。

File 对象是代表用户选择的文件,它是 Blob 对象的一个扩展,具有一些额外的属性,比如namelastModified等。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,可以读取FileBlob对象的内容,支持多种格式,包括文本和二进制。

常用方法:

  • 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),如 Uint8ArrayFloat32Array 等,它们允许在缓冲区中以特定类型的方式读取和操作数据。

js 复制代码
const buffer = new ArrayBuffer(16); // 创建一个 16 字节的 ArrayBuffer
const uint8View = new Uint8Array(buffer); // 创建一个视图
uint8View[0] = 255; // 操作视图中的数据
console.log(uint8View[0]); // 输出 255

5. 处理二进制数据

通过 FileReaderArrayBuffer,我们可以读取并处理文件中的二进制数据。例如,读取一个图像文件并显示:

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 之间的转换 (常见)

FileBlob 的子类型,因此可以直接将一个 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 是一个用于创建指向 BlobFile 对象的 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,因为 fetchAxios也是一样的) 会自动处理,如果你需要手动设置Content-Type也是可以的,但是要注意:手动设置Content-Type时边界字符串不会自动添加,这会导致服务器无法正确解析请求。因此,通常情况下不建议手动设置 Content-Type

乱码流的概念

"乱码流"并不是一个标准的技术术语,但在实际开发中,它通常指的是由于编码问题导致的数据在传输或显示过程中出现的乱码现象。这种现象通常发生在以下几个场景中:

  1. 字符编码不匹配: 发送方和接收方使用的字符编码不一致。
  2. 数据损坏: 数据在传输过程中被损坏或丢失。
  3. 格式错误: 数据格式不符合预期,导致解析错误。

乱码流的数据类型

乱码流本质上仍然是二进制数据或文本数据,具体类型取决于原始数据的格式。常见的数据类型包括:

  • 二进制数据 (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.parseJSON.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');
    }
  });
});

总结

  • 乱码流通常是由于字符编码不匹配、数据损坏或格式错误导致的。
  • 处理方法包括确保字符编码一致、使用校验和验证数据完整性和确保数据格式正确。
  • 数据类型可以是二进制数据或文本数据,具体取决于原始数据的格式。
相关推荐
℘团子এ2 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z8 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁31 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜32 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish33 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple33 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five34 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫54135 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
酷酷的威朗普36 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省36 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript