js判断文件类型详解

文章介绍了如何使用JavaScript来判断文件类型,包括通过file对象的type属性、文件后缀以及通过读取文件的二进制流和检查文件头来确定文件格式。这种方法能更准确地识别文件类型,防止被篡改后缀的文件上传。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

js判断文件类型详解

通过file的type属性判断

<input type="file" onchange="onchangecb(this)" />
<script>
function onchangecb(e) {
  const file = e.files[0];
  console.log(file.type);
}
</script>

htmlinput标签,就是根据选择文件的后缀来生成一个File对象。

像下面的几种文件:

txt文件:
在这里插入图片描述

jpg文件:
在这里插入图片描述

mp4文件:

在这里插入图片描述

使用文件后缀来判断

function onchangecb(e) {
  const file = e.files[0];
  //获取最后一个.的位置
  const index= file.name.lastIndexOf(".");
  //获取后缀
  const ext = file.name.substr(index+1);
  console.log(ext);
}

在得到文件后缀名后,根据后缀即可判断文件的类型(文件格式)。比如需要判断一个文件是否是图片格式,首先定义一个判断函数:

function isImage(ext) {
 return [
 'png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff'].
 indexOf(ext.toLowerCase()) !== -1;
}

虽然可以直接通过对象的type属性或者文件后缀来获取文件类型,但是如果强行修改文件后缀,同样可以将其他类型的文件上传至服务器,或者文件压根就没有后缀,那又要怎么判断呢?因此前端需要使用一个更加合理的方式。

根据二进制流及文件头来判断

虽然文件后缀可以手动改,因此可以直接通过读取文件的二进制来判断。

通常来说固定类型的文件头都是相同的,比如说jpeg的文件头是FF D8 FF E0

这里提供一些常用的文件头:

const fileMap = {
  JPEG: "FF D8 FF E0",
  JPG: "FF D8 FF E1",
  PNG: "89 50 4E 47",
  GIF: "47 49 46 38",
  TIFF: "49 49 2A 00",
  BMP: "42 4D",
  DWG: "41 43 31 30",
  PSD: "38 42 50 53",
  RTF: "7B 5C 72 74 66",
  XML: "3C 3F 78 6D 6C",
  HTML: "68 74 6D 6C 3E",
  EML: "44 65 6C 69 76 65 72 79 2D 64 61 74 65 3A",
  DBX: "CF AD 12 FE C5 FD 74 6F",
  PST: "21 42 44 4E",
  XLS: "D0 CF 11 E0",
  DOC: "D0 CF 11 E0",
  MDB: "53 74 61 6E 64 61 72 64 20 4A",
  WPD: "FF 57 50 43",
  PDF: "25 50 44 46 2D 31 2E",
  QDF: "AC 9E BD 8F",
  PWL: "E3 82 85 96",
  ZIP: "50 4B 03 04",
  RAR: "52 61 72 21",
  WAV: "57 41 56 45",
  AVI: "41 56 49 20",
  RAM: "2E 72 61 FD",
  RM: "2E 52 4D 46",
  MPG: "00 00 01 BA",
  MPG: "00 00 01 B3",
  MOV: "6D 6F 6F 76",
  ASF: "30 26 B2 75 8E 66 CF 11",
  MID: "4D 54 68 64",
  MP3: "49 44 33",
};

通过onchange方法获取到选择的文件后,使用FileReader读取文件的二进制,之后判断二进制的前几位是否跟符合相应类型文件的文件头。

以下是一些使用文件头来判断文件类型的简单代码

// 判断文件后缀与该文件内容是否相同类型
async function isSameFileType(file) {
  const suffix = getFileSuffix(file.name).toUpperCase();
  const fileBlobString = await blobToString(file.slice(0, 14));
  // 如果文件类型中没有该类型,默认为false
  if (!(suffix in fileMap)) {
    return false;
  }

  return fileBlobString.includes(fileMap[suffix]);
}

async function blobToString(blob) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = function () {
      const ret = reader.result
        .split("") // 分隔开
        .map((v) => v.charCodeAt()) // 循环返回指定位置的字符的 Unicode 编码
        .map((v) => v.toString(16).toUpperCase()) // 返回十六进制格式
        .map((v) => v.padStart(2, "0")) // 给空的那个填充 00 ,防止空缺
        .join(" "); // 每个子节之间空格隔开
      resolve(ret);
    };
    reader.readAsBinaryString(blob); // 调用之后触发onload事件
  });
  // 二进制=》ascii码=》转成16进制字符串
}

// 获取文件后缀
function getFileSuffix(filename) {
  return filename.substring(filename.lastIndexOf(".") + 1, filename.length);
}

判断是否是png类型:

async function isPng(file) {
  // 同理
  const ret = await blobToString(file.slice(0, 4));
  const ispng = ret === fileMap.PNG;
  return ispng;
}

当然也可以针对图片高宽进行限制:
png二进制中,第18-20位是图片的宽,22-24是高。

获取图片宽高:

function getRectByOffset(file, widthOffset, heightOffset, reverse) {
  let width = await blobToString(file.slice(...widthOffset));
  let height = await blobToString(file.slice(...heightOffset));
  
  const w = parseInt(width.replace(" ", ""), 16);
  const h = parseInt(height.replace(" ", ""), 16);
  return { w, h };
}

修改一下之前判断isPng的方法,加上高宽限制

const IMG_WIDTH_LIMIT = 1000;
const IMG_HEIGHT_LIMIT = 800
function isPng(file) {
  // 同理
  const ret = await this.blobToString(file.slice(0, 4));
  const ispng = ret === fileMap.PNG;
  if (ispng) {
    const { w, h } = await this.getRectByOffset(file, [18, 20], [22, 24]);
    console.log(`png 宽高 ${w},${h}`);
    if (w > IMG_WIDTH_LIMIT || h > IMG_HEIGHT_LIMIT) {
      this.$message.error(
        "png图片宽高不得超过 " + IMG_WIDTH_LIMIT + "和" + IMG_HEIGHT_LIMIT
      );
      return false;
    }
  }
  return ispng;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值