You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Node.js+TypeScript检测WAV文件是否有效或已损坏

WAV文件校验逻辑优化方案

现有代码存在的问题

  • 缺少Buffer最小长度前置校验:如果传入的Buffer长度小于44字节,调用readIntLE等方法会直接抛出异常,导致服务报错
  • 固定写死data块起始位置为44字节:仅适用于无扩展块的标准PCM格式WAV,如果文件包含LIST元数据块、非PCM格式的fmt扩展块,data块的起始位置会偏移,此时计算realDataLength的逻辑完全错误
  • 存在浮点数精度风险:用除法计算校验值时如果出现小数,和整数存储的byteRate、blockAlign比对可能出现意料之外的不匹配
  • 缺少音频格式校验:未校验fmt块中的音频格式字段,若收到压缩格式的WAV(比如MP3编码的WAV封装),即使头信息校验通过也不符合常规业务的使用要求
  • 缺少采样精度合法范围校验:未校验bitsPerSample的常见合法值,出现异常值时无法识别

优化后代码

function isWavValid(fileBuffer: Buffer): boolean {
  // 前置校验最小长度,避免读取越界
  const MIN_WAV_LENGTH = 44;
  if (fileBuffer.length < MIN_WAV_LENGTH) return false;

  // 基础头标识校验
  if (fileBuffer.toString('utf8', 0, 4) !== 'RIFF') return false;
  if (fileBuffer.toString('utf8', 8, 12) !== 'WAVE') return false;
  if (fileBuffer.toString('utf8', 12, 16) !== 'fmt ') return false;

  // 校验音频格式,1代表未压缩PCM,如有其他格式需求可调整允许范围
  const audioFormat = fileBuffer.readIntLE(20, 2);
  if (audioFormat !== 1) return false;

  // 读取基础参数
  const numChannels = fileBuffer.readIntLE(22, 2);
  const sampleRate = fileBuffer.readIntLE(24, 4);
  const byteRate = fileBuffer.readIntLE(28, 4);
  const reportedBlockAlign = fileBuffer.readIntLE(32, 2);
  const bitsPerSample = fileBuffer.readIntLE(34, 2);

  // 参数合法性校验
  if (numChannels < 1 || numChannels > 65535) return false;
  // 校验采样精度为常见合法值,可根据业务需求调整允许范围
  const validBitsPerSample = [8, 16, 24, 32];
  if (!validBitsPerSample.includes(bitsPerSample)) return false;
  if (byteRate !== sampleRate * numChannels * bitsPerSample / 8) return false;
  if (reportedBlockAlign !== numChannels * bitsPerSample / 8) return false;

  // 动态查找data块起始位置,兼容带扩展块的WAV
  let dataStartOffset = 36;
  let foundDataChunk = false;
  let reportedDataLength = 0;
  while (dataStartOffset + 8 <= fileBuffer.length) {
    const chunkId = fileBuffer.toString('utf8', dataStartOffset, dataStartOffset + 4);
    const chunkSize = fileBuffer.readIntLE(dataStartOffset + 4, 4);
    if (chunkId === 'data') {
      foundDataChunk = true;
      reportedDataLength = chunkSize;
      break;
    }
    // 跳过当前块,块长度如果是奇数需要补1个字节对齐
    dataStartOffset += 8 + (chunkSize % 2 === 0 ? chunkSize : chunkSize + 1);
  }
  if (!foundDataChunk) return false;

  // 校验RIFF块大小
  const reportedChunkSize = fileBuffer.readIntLE(4, 4);
  if (reportedChunkSize !== fileBuffer.length - 8) return false;

  // 校验实际数据长度匹配
  const realDataLength = fileBuffer.length - (dataStartOffset + 8);
  if (reportedDataLength !== realDataLength) return false;

  return true;
}

补充说明

如果你的业务明确只收无扩展块的标准PCM WAV,不需要兼容带元数据的文件,可以去掉动态查找data块的逻辑,保留固定偏移的写法即可,性能更高。如果需要做更严格的损坏校验,可以进一步读取data块的内容,校验每个采样点是否在对应采样精度的合法取值范围内,但这个操作对大文件性能损耗较大,可以根据业务场景选择是否开启。

内容的提问来源于stack exchange,提问作者Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 09:39:03