为什么要用十六进制来判断文件类型?
举一个例子,代码如下,通过该代码实现一个简单的Input上传文件,并输出打印文件解析的内容。
<template>
<div class="">十六进制-判断文件类型</div>
<input type="file"
@change="inputFilefn($event)">
</template>
<script setup>
const inputFilefn = (e) => {
console.log(e.target.files[0]);
}
</script>
<style lang="less" scoped>
</style>
准备一个图片类型的上传文件
点击浏览器页面的选择文件按钮,将该文件上传,在控制器中得到打印结果,得到的文件类型是 image/png
但是如果把该文件名后缀改成.mp3格式再进行上传,可以发现得到的文件类型是 audio/mpeg,但是我们都知道这个元件里的内容应该是image/png类型的,所以说这个上传文件判断文件的类型并不是很靠谱,可以通过修改文件的后缀欺骗校验。
解决办法
可以通过查看文件的二进制数据来识别其真实的类型,因为计算机操作系统在识别文件类型时,并不看文件的后缀名,而是通过:Magic number(魔幻数),来判断文件类型。
很多文件类型起始几个字节的内容是固定的。
测试一下,用EditPlus打开我们所准备的文件,以hex格式查看前几个数据,这几个数据的内容被称为Magic number(魔幻数),通过比较这几个字节的内容可以确定文件类型。
由图可以看出,之前被修改后缀名为mp3格式的文件现在放入EditPlus解析后第一行中可以看出,解析出来的文件类型是原始文件的类型 png。
再准备一个图片文件left.png 导入进EditPlus进行解析,通过对比上一个文件解析的结果,只关注第一行解析结果,那就是两个文件的前8个字节是一样的,这也证实了同样类型的文件开头前几个字节是相同的。
代码实现
<template>
<div class="">十六进制-判断文件类型</div>
<input type="file"
@change="inputFilefn($event)">
</template>
<script setup>
const inputFilefn = (e) => {
// console.log(e.target.files);
const [file] = e.target.files
// console.log(file);
const fileReader = new FileReader()
// 截取指定部分的内容 fileReader.readAsArrayBuffer(file)
// 因为只需要比较前8个字节,所以只需要截取前8个元素
fileReader.readAsArrayBuffer(file.slice(0, 8))
fileReader.onload = function (e) {
// e 事件对象, 用于监控读取进度。loaded、total
console.log(e);
// result 属性中保存的是被读取文件的 ArrayBuffer
// ArrayBuffer对象代表存储二进制数据的一段内存,是原始的二进制数据,不能直接读写
// 1.TypedArray 视图用来读写简单类型的二进制数据
// 2.DataView 视图用来读写复杂类型的二进制数据
console.log(fileReader.result);
let result = fileReader.result
// 转为 TypedArray 格式,用来操作 ArrayBuffer数据
const uint8Arr = new Uint8Array(result)
console.log(uint8Arr);
// 定义一个【预先要比较的png格式的十六进制文件头】数组
// 最常用的表示十六进制数值方式,将'0x'加在数字前面
let pngHex = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
/*
every(),循环判断指定函数检测数组中的所有元素:
如果数组中检测到有一个元素不满足,则整个表达式返回false,且剩余数组元素不会再继续循环检测
如果所有元素都满足条件,则返回true
*/
const isCheckFn = (hObj, uObj) => {
return hObj.every((hobj_n, inx) => {
return hobj_n === uObj[inx]
})
}
// 比较pngHex 和 uint8Arr 中的内容是否完全一样来判断该文件是哪种类型。
console.log(isCheckFn(pngHex, uint8Arr) ? '是png格式' : '不是Png格式')
}
}
</script>
<style lang="less" scoped>
</style>
打开浏览器上传文件
分别上传改动后的right.mp3格式的文件和left.png格式的文件,解析出来的结果都是png格式,达到了我们的需求。
更多类型
本次案例代码,只进行对png格式类型的判断,如果想对多种文件类型进行判断,所以下面给出常用的文件类型格式的文件头二进制标识来丰富文件判断类型,可以加在案例代码中丰富功能。
常见文件二进制标识:
JPEG/JPG - 文件头标识(2 字节):ff,d8 文件结束标识(2 字节):ff,d9
TGA - 未压缩前 (5 字节):00 00 02 00 00
PNG未压缩前 (8 字节):89 50 4E 47 0D 0A 1A 0A
GIF- 未压缩前 (6 字节):47 49 46 38 39(37) 61
BMP- 未压缩前 (2 字节):42 4D B M
PCX- 未压缩前 (1 字节):0A
TIFF- 未压缩前 (2 字节):4D 4D 或 49 49
ICO- 未压缩前 (8 字节):00 00 01 00 01 00 20 20
CUR- 未压缩前 (8 字节):00 00 02 00 01 00 20 20
IFF- 未压缩前 (4 字节):46 4F 52 4D
ANI- 未压缩前 (4 字节):52 49 46 46