如何正确检测文件类型?

        在上传文件时,在某些情况下我们希望能限制文件上传的类型,比如限制只能上传 PNG 格式的图片。我们可以通过 input元素的 accept 属性来限制上传的文件类型,

例如:

<input type="file" accept="image/png" />

这种方案是通过识别文件到后缀名 .png 来限制。如果用户把 JPEG 格式的图片后缀名更改为 .png 的话,就可以成功突破这个限制。为了更严格的限制,我们可以通过读取文件的二进制数据来识别正确的文件类型。

一、如何查看文件的二进制数据

        要查看文件对应的二进制数据,我们可以借助一些现成的编辑器,比如 Windows 平台下的 WinHex 或 macOS 平台下的 Synalyze It! Pro 十六进制编辑器。这里我们使用 Synalyze It! Pro 这个编辑器,以十六进制的形式来查看PNG 图片对应的二进制数据。

二、如何区分图片的类型

计算机并不是通过图片的后缀名来区分不同的图片类型,而是通过 “魔数”(Magic Number)来区分。 对于某一些类型的文件,起始的几个字节内容都是固定的,根据这几个字节的内容就可以判断文件的类型。

常见图片类型对应的魔数如下表所示:

文件类型文件后缀魔数
JPEGjpg/jpeg0xFF D8 FF
PNGpng0x89 50 4E 47 0D 0A 1A 0A
GIFgif0x47 49 46 38(GIF8)
BMPbmp0x42 4D

由上图可知,PNG 类型的图片前 8 个字节是 0x89 50 4E 47 0D 0A 1A 0A。当你把 .png 文件修改为 .jpeg 后,再用编辑器打开查看图片的二进制内容,文件的前 8 个字节还是保持不变。

 

三、Javascript 如何检测图片类型

<div>
  选择文件:
  <input type="file" id="inputFile" accept="image/*" @change="handleChange" />
  <p>{{ fileType }}</p>
</div>
data() {
  return {
    fileType: ''
  }
},
methods: {
    readBuffer(file, start = 0, end = 2) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
          resolve(reader.result);
        };
        reader.onerror = reject;
        reader.readAsArrayBuffer(file.slice(start, end));
      });
    },
    check(headers) {
      return (buffers, options = { offset: 0 }) =>
        headers.every(
          (header, index) => header === buffers[options.offset + index]
        );
    },

    async handleChange(event) {
      const isPNG = this.check([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); // PNG图片对应的魔数
      const file = event.target.files[0];
      const buffers = await this.readBuffer(file, 0, 8);
      const uint8Array = new Uint8Array(buffers);
      this.fileType = `${file.name}文件的类型是:${isPNG(uint8Array) ? "image/png" : file.type}`
      console.log(file)
    }
  }

以上示例将 PNG 图片后缀改为 .jpg 后,对应的检测结果如下图所示:

但在实际工作中,遇到的文件类型是多种多样的,可以使用现成的第三库来实现文件检测的功能,比如 file-type 这个库。

四、参考资料

玩转前端二进制

MDN - FileReader

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值