最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端——利用File signature精准校验文件类型

    正文概述 掘金(62921)   2021-03-20   568

    背景:在进行文件上传时,往往需要对上传文件的类型进行限制。最简单也是最常用的文件类型校验方法,是直接校验文件的拓展名,但由于拓展名可以手动随意修改,因此这种方式并不保险。那么有没有什么方法可以准确地判断出上传文件的类型呢?

    File signature

    File signature是指在文件中用于标识文件格式的字节,通常在文件的开头放置一小段字节(大多数为2-4个字节)。不同的文件类型都有着对应的文件签名,通过 List of file signatures 和 All File Signatures我们可以查询到各个文件类型的签名。

    通过File signature校验文件类型

    检验思路

    1. 获取文件,并将文件转化为ArrayBuffer

    文件可通过网络获取或本地获取,对应的转化方法:

    /**
    * 获取网络文件并转化为ArrayBuffer
    */
    function loadFile(fileUrl) {
     return new Promise((resolve, reject) => {
       const xhr = new XMLHttpRequest();
       xhr.onload = function () {
         if (xhr.status === 200) {
           resolve(xhr.response);
         }
       };
       xhr.onerror = reject;
       xhr.open('GET', fileUrl, true);
       xhr.responseType = 'arraybuffer';
       xhr.send('');
     })
    }
    
    /**
    * 获取本地文件并转化为ArrayBuffer
    */
    
    //input[type="file"].onchange 回调事件
    function onFileChange(file) {
      return new Promise(async (reslove, reject) => {
        if (!file) {
          return reslove(false);
        }
       const FR = new FileReader();
       const fileChunk = file.slice(offset, size + offset);
       FR.readAsArrayBuffer(fileChunk);
       FR.onload = (e) => {
           //e.target.result便是所需ArrayBuffer
           const ArrayBuffer = e.target.result;
          //do something
        }
      });
    }
    
    1. 将buffer转化为16进制字符串

    通过TypedArray进行转化:

    Array.prototype.map.call(new Uint8Array(ArrayBuffer), (x) => x.toString(16).padStart(2, '0')).join('');
    

    但TypedArray将会使用系统默认的字节顺序(详情见TypedArray or DataView: Understanding byte order),而使用DataView 则会默认使用从大到小的顺序:

    const view = new DataView(ArrayBuffer);
    let hex= '';
    for (let i = 0; i < view.byteLength; i += 1) {
      let byte = Number(view.getUint8(i)).toString(16).toUpperCase();
      byte.length === 1 && (byte = '0' + byte);
      hex += byte;
    }
    
    1. 根据文件后缀名截取对应文件签名并进行比对

    如针对pdf的文件签名

        {
          extension: 'ppt',
          signature: '006E1EF0',
          size: 4,
          offset: 512,
        },
    

    可截取16进制字符串中的第offset到offset+size个个byte与signature进行比对

    const fileSignature = hex.slice(offset * 2, (size + offset) * 2);
    

    优化

    可根据文件的后缀名得到对应文件签名的offset与size,直接截取文件中文件签名的内容,而无需加载文件的全部二进制数据。

    完整代码

    //文件签名列表,作为文件上传类型的白名单
    const fileSignatures = {
      pdf: [
        {
          extension: 'pdf',
          signature: '25504446',
          size: 4,
          offset: 0,
        },
      ],
      //....
    };
    
    
    function getfileSignature(file, size, offset) {
      return new Promise((resolve, reject) => {
        try {
          const FR = new FileReader();
          const fileChunk = file.slice(offset, size + offset);
          FR.readAsArrayBuffer(fileChunk);
          FR.onload = (e) => {
            const view = new DataView(e.target.result as ArrayBufferLike);
            let fileSignature = '';
            for (let i = 0; i < view.byteLength; i += 1) {
              let byte = Number(view.getUint8(i)).toString(16).toUpperCase();
              byte.length === 1 && (byte = '0' + byte);
              fileSignature += byte;
            }
    
            resolve(fileSignature);
          };
        } catch (e) {
          reject(e);
        }
      });
    }
    
    function judgeFileType(file) {
      return new Promise(async (reslove, reject) => {
        if (!file) {
          return reslove(false);
        }
        try {
          const fileInfoArr = file.name.split('.');
          const fileType = fileInfoArr[fileInfoArr.length - 1];
    
          if (fileSignatures[fileType]) {
            for (let i = 0; i < fileSignatures[fileType].length; i++) {
              const fileSignature = await getfileSignature(file, fileSignatures[fileType][i].size, fileSignatures[fileType][i].offset);
              if (fileSignature === fileSignatures[fileType][i].signature) {
                return reslove(true);
              } else if (i === fileSignatures[fileType].length - 1) {
                return reslove(false);
              }
            }
          } else {
            return reslove(false);
          }
        } catch (e) {
          reject(e);
        }
      });
    }
    

    起源地下载网 » 前端——利用File signature精准校验文件类型

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元