如何严格判断文件上传类型(再不会你就out啦!)
文件上传是工作中常见的业务需求,很多情况下,我们需要限制文件的上传类型,比如只能上传图片。通常我们是通过input
元素的accept
属性来限制文件的类型:
或者通过截取文件名后缀的方式来判断:
const ext = file.name.substring(file.name.lastIndexOf('.') + 1);
这样做看似没有毛病,但如果把其他文件的后缀名改为图片格式,就可以成功突破这个限制。以上两种方式都不严谨,存在一定的安全隐患。那么应该如何解决这个问题呢?
一、查看文件的头信息
所有文件在计算机中都是以二进制形式进行存储的,但二进制数据是不方便做判断的,我们可以利用 vscode 插件
hexdump for VSCode
以十六进制的形式查看二进制文件。安装完成后,点击右上角的小图标,即可查看文件的十六进制信息:文章图片
那么,我们分别查看一下
jpg png gif
的十六进制头信息:文章图片
文章图片
文章图片
多打开几个文件试试,你会发现同一种类型的文件,他们的头信息是完全相同的。接下来,我们就可以根据头信息来判断文件类型了。
二、根据头信息判断文件类型
1. 将文件转为十六进制字符串 在获取文件对象后,我们可以通过
FileReader API
来读取文件的内容,然后将结果转为Unicode
编码,再转为十六进制,以下是我封装的将文件转为十六进制字符串的方法:async blobToString(blob) {
return new Promise(resolve => {
const reader = new FileReader()
reader.onload = function() {
const res = reader.result
.split("") // 将读取结果分割为数组
.map(v => v.charCodeAt()) // 转为 Unicode 编码
.map(v => v.toString(16).toUpperCase()) // 转为十六进制,再转大写
.map(v => v.padStart(2, "0")) // 个位数补0
.join(" ");
// 转为字符串
resolve(res)
}
reader.readAsBinaryString(blob) // 将文件读取为二进制字符串
})
}
2. 判断文件类型 其实没有必要将整个文件转为十六进制,我们只需要截取文件的前几个字节,然后将截取后的文件转为十六进制,再进行比对就可以了:
// 判断是否为 jpg 格式
async function isJpg(file) {
const res = await blobToString(file.slice(0, 3))
return res === 'FF D8 FF'
}
// 判断是否为 png 格式
async function isPng(file) {
const res = await blobToString(file.slice(0, 4))
return res === '89 50 4E 47'
}
// 判断是否为 gif 格式
async function isGif(file) {
const res = await blobToString(file.slice(0, 4))
return res === '47 49 46 38'
}
3. 完整代码
Document - 锐客网
三、总结
通过文件头信息,我们除了可以判断文件的类型,还可以读取文件相关的元信息,比如图片的尺寸、位深度、色彩类型和压缩算法等,只是这些信息所在的位置不一样。
按照以上方式,大家同样可以判断其他格式的文件,常用文件的文件头。如果你还嫌麻烦,可以使用现成的第三库来实现这个功能,比如 file-type 这个库,有兴趣的同学可以试一试。
【如何严格判断文件上传类型(再不会你就out啦!)】看完记得点个赞呦!万事开头难,听说喜欢点赞的你在 2022 年将迎来一个开门红!
推荐阅读
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 如何寻找情感问答App的分析切入点
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus使用queryWrapper如何实现复杂查询
- 如何在Mac中的文件选择框中打开系统隐藏文件夹
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- java中如何实现重建二叉树
- Linux下面如何查看tomcat已经使用多少线程
- thinkphp|thinkphp 3.2 如何调用第三方类库
- 2019女表什么牌子好(如何挑选女士手表?)