如何严格判断文件上传类型?再不会你就out啦!

如何严格判断文件上传类型?再不会你就out啦!文件上传是工作中常见的业务需求,很多情况下,我们需要限制文件的上传类型,比如只能上传图片。通常我们是通过input元素的accept属性来限制文件的类型, 或者通过截取文件名后缀的方式来判断:

文件上传是工作中常见的业务需求,很多情况下,我们需要限制文件的上传类型,比如只能上传图片。通常我们是通过input元素的accept属性来限制文件的类型:

<input id="file" type="file" accept="image/*" />

或者通过截取文件名后缀的方式来判断:

const ext = file.name.substring(file.name.lastIndexOf('.') + 1);

这样做看似没有毛病,但如果把其他文件的后缀名改为图片格式,就可以成功突破这个限制。以上两种方式都不严谨,存在一定的安全隐患。那么应该如何解决这个问题呢?

一、查看文件的头信息

所有文件在计算机中都是以二进制形式进行存储的,但二进制数据是不方便做判断的,我们可以利用 vscode 插件hexdump for VSCode以十六进制的形式查看二进制文件。安装完成后,点击右上角的小图标,即可查看文件的十六进制信息:

01.jpg

那么,我们分别查看一下jpg png gif的十六进制头信息:

image.png image.png image.png

多打开几个文件试试,你会发现同一种类型的文件,他们的头信息是完全相同的。接下来,我们就可以根据头信息来判断文件类型了。

二、根据头信息判断文件类型

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. 完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input id="file" type="file" />
    <script> file.addEventListener('change', async e => { const file = e.target.files[0] const flag = await isImage(file) if (flag) { alert('上传格式通过!') } else { alert('请上传正确的格式!') } }) // 判断是否为图片 async function isImage(file) { return (await isGif(file)) || (await isPng(file)) || (await isJpg(file)) } // 判断是否为 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' } // 将文件转为十六进制字符串 async function 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) // 将文件读取为二进制字符串 }) } </script>
  </body>
</html>

三、总结

通过文件头信息,我们除了可以判断文件的类型,还可以读取文件相关的元信息,比如图片的尺寸、位深度、色彩类型和压缩算法等,只是这些信息所在的位置不一样。

按照以上方式,大家同样可以判断其他格式的文件,常用文件的文件头。如果你还嫌麻烦,可以使用现成的第三库来实现这个功能,比如 file-type 这个库,有兴趣的同学可以试一试。

看完记得点个赞呦!万事开头难,听说喜欢点赞的你在 2022 年将迎来一个开门红!😉

Demo地址: github.com/frontend-af…

往期精彩

10个常见的前端手写功能,你全都会吗?

Element-UI 奇淫技巧第二弹!提升开发效率,延长摸鱼时间~

听说你还不会虚拟列表?原谅我来晚了

一款强大到没朋友的图片编辑插件,爱了爱了!

如何实现拖拽上传、上传进度条,以及取消上传?

今天的文章如何严格判断文件上传类型?再不会你就out啦!分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22324.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注