文件类型判断

1,602 阅读2分钟

前言

在日常项目,文件上传是一个非常常见的需求,但是文件类型的判断确实一个大难题。在我使用element-ui里面的upload进行判断时,官方的例子:

beforeAvatarUpload(file) {
   const isJPG = file.type === 'image/jpeg';
}

但是在我的使用过程中通过file.type也就是常说的MIME(多用途互联网邮件拓展类型)进行判断的话,会出现以下问题:

1.个别文件在mac和windows上传获取到的MIME类型不同。

由此我们开始使用了文件名称的后缀去做判断: 、

beforeAvatarUpload(file) {
      const splitList = file.name.split('.')
      const type = splitList[splitList.length - 1].toLocaleLowerCase()

      const typeList = ['rar', 'zip', 'doc', 'docx', 'pdf', 'jpg', 'png']
      const isTrueFileType = typeList.some(item => item === type)
      if (!isTrueFileType) {
        this.$message.error('文件格式不对')
      }
}

使用文件后缀做判断同样有个问题,当一个文件的后缀名称进行改动,则无法上传,无法判断文件的真实类型。 最终我们使用了读取文件二进制的数据来进行文件类型判断。

js部分代码

function readBuffer(file, start, end) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(file.slice(start, end));
  });
}

  • file对象是继承blob对象的,所以file.slice(start, end)可以直接获取file对象中从start到end的字节。
  • FileReader.readAsArrayBuffer是FileReader提供的内置方法, 异步按字节读取文件内容,结果用ArrayBuffer对象表示。
function check(normalHeaders) {
  return (buffers) => {
    return normalHeaders.every((header, index) => {
      return header == buffers[index];
    });
  };
}
const isPNG = check([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);

check方法主要是对比当前传入文件buffers和基础normalHeaders,运用的函数的柯里化,返回一个比较函数。

总结

以上两个方法就可以简单实现通过blob真实对比一个文件的真实类型,其原理还是通过获取文件的二进制数据时,头部的字节保持不变,进而对比文件的类型。使用方法类似:

    const handleChange = async event => {
      let file = event.target.files[0];
      let buffer = await readBuffer(file, 0, 8);
      let uint8Array = new Uint8Array(buffer);
      isPNG(uint8Array)
    };