# 如何检测文件类型
# 背景
- 希望限制文件上传的类型。比如在【Epub电子书阅读器】项目中,只希望解析的是epub格式的文件。
- 虽然可以使用后缀名可以解决,但是也可以通过读取文件的二进制数据来判断文件的类型。
# 如何获取文件的二进制数据
- 编辑器工具
- Windows: WindHex
- MacOS: Synalyze It! Pro十六进制编辑器
# 怎么区分不同文件类型的
- 通过魔数(Magic Number),某一类型文件的起始几个字节内容都是固定的,根据这些字节内容就可以判断文件类型
- 例如:PNG图片的前8个字节是0x89 50 4E 47 0D 0A 1A 0A,修改为JPEG格式后,图片前8个字节依旧不变
# 具体实现
选择文件:<input type="file" name="" id="inputFile" accept="image/*" onchange="handleChange(event)">
<p id="realFileType"></p>
1
2
2
const isPNG = check([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);//PNG图片对应的魔数
const realFileElement = document.querySelector('#realFileType');
async function handleChange(event) {
const file = event.target.files[0];
const buffers = await readBuffer(file, 0, 8);//ArrayBuffer数据对象
const uint8Array = new Uint8Array(buffers)
realFileElement.innerHTML = `${file.name}文件的类型是:${isPNG(uint8Array) ? "image/png" : file.type}`
}
function readBuffer(file, start = 0, end = 2) {//读取文件指定范围的二进制数据
return new Promise((resolve, reject) => {
const reader = new FileReader(); //通过FileReader读取文件内容
reader.onload = () => {
resolve(reader.result)
}
reader.onerror = reject;
reader.readAsArrayBuffer(file.slice(start, end))
})
}
function check(headers) {
return (buffers, options = { offset: 0 }) =>
headers.every(
(header, index) => header === buffers[options.offset + index]
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 涉及到的背景知识
- FileReader.readAsArrayBuffer()
- FileReader异步读取用户计算机上的文件
- readAsArrayBuffer一旦完成,result属性中保存的是将被读取文件的ArrayBuffer数据对象
- Unit8Array
- new Uint8Array(buffer [, byteOffset [, length]]);
- 8位无符号整型数组,创建完成后可以以对象/数组下标索引方式引用数组中的元素
- Uint8Array.prototype.every()
- promise;async/await
- 柯里化
- check函数为了实现逐字节对比+复用
function check(headers){ return (buffers,options)=>{} }
1
2
3