正常情况下,文件都是有后缀名的,例如:坤坤运球.gif, 坤坤跳舞.avi

但是,也有无后缀名的文件。尝试着把 坤坤运球.gif 的文件后缀去掉,然后用图片查看器打开该文件,同样能看到坤坤精湛的球技。

这说明,改变文件的后缀名,并不会影响文件本身的内容。同时也说明,文件的数据中,有东西能够标识出文件的类型。

说以上那么多,其实是想引出 Magic Number 的概念。

简单的来说,Magic Number 由文件头中前几个字节组成, 同一类型的文件这几个字节都是一样的,故而可以用来标识出一个文件的类型。由于这串数字从字面上来看是无法理解的,故而称之为 Magic Number,原名叫做:File Signatures(文件签名)。

利用 Magic Number 判断文件类型

首先这里有一个文件url,告诉你这是一个音频文件,你能看出它是什么类型的音频文件吗?

  1. const url = 'https://0345-1400187352-1256635546.cos.ap-shanghai.myqcloud.com/rychou/e3801cfc517873a5a5471241e1da1869'

很显然是看不出来的,因为这个文件没有后缀。接下来看看如何利用 Magic Number 来判断这个音频文件的类型。

找到不同类型文件的 Magic Number

可以参考 Magic Number Table 中的数据,找到不同类型文件的 Magic Number。
例如:搜索 mp3, 找到 mp3 文件类型的 Magic Number 为: 49 44 33。注意这是个 16 进制数。

  1. // 常见的音频文件的 magic number
  2. const hexMap = {
  3. '494433': 'mp3',
  4. '664c614300000022': 'flac',
  5. '2321414d52': 'amr',
  6. 'fff1': 'aac',
  7. 'fff9': 'aac',
  8. '4f67675300020000000000000000': 'oga',
  9. '52494646xxxxxxxx57415645666d7420': 'wav',
  10. '3026b2758e66cf11a6d900aa0062ce6c': 'wma'
  11. }
  12. // 利用 16 进制字符串判断类型
  13. const isAAC = hex => !!hexMap[hex.slice(0, 4)]
  14. const isMP3 = hex => !!hexMap[hex.slice(0, 6)]
  15. const isAMR = hex => !!hexMap[hex.slice(0, 10)]
  16. const isFLAC = hex => !!hexMap[hex.slice(0, 16)]
  17. const isWAV = hex => !!hexMap[hex.slice(0, 32)]
  18. const isWMA = hex => !!hexMap[hex.slice(0, 32)]
  19. const isOGA = hex => !!hexMap[hex.slice(0, 28)]

找到了文件的 Magic Number,要判断文件的类型就很简单了。

请求获取文件数据,并转成 Array Buffer

  1. /**
  2. * 加载文件
  3. * @param {string} url - audio file url
  4. * @returns {Promise}
  5. */
  6. function loadFile(url) {
  7. return new Promise((resolve, reject) => {
  8. const xhr = new XMLHttpRequest()
  9. xhr.onreadystatechange = function() {
  10. if (xhr.readyState === 4) {
  11. resolve(xhr)
  12. }
  13. }
  14. xhr.onerror = reject
  15. xhr.open('GET', url, true)
  16. xhr.responseType = 'arraybuffer'
  17. xhr.send('')
  18. })
  19. }

buffer 转 16 进制字符串

  1. /**
  2. * buffer 转 16 进制字符串
  3. * @param {ArrayBuffer} buffer
  4. * @returns
  5. */
  6. function buf2hex(buffer) {
  7. // buffer is an ArrayBuffer
  8. return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('')
  9. }

判断类型

  1. function getAudioType(url) {
  2. return loadFile(url).then(xhr => {
  3. const hex = buf2hex(buffer)
  4. if (isAAC(hex)) {
  5. return 'aac'
  6. } else if (isAMR(hex)) {
  7. return 'amr'
  8. } else if (isMP3(hex)) {
  9. return 'mp3'
  10. } else if (isFLAC(hex)) {
  11. return 'flac'
  12. } else if (isWAV(hex)) {
  13. return 'wav'
  14. } else if (isWMA(hex)) {
  15. return 'wma'
  16. } else if (isOGA(hex)) {
  17. return 'oga'
  18. } else {
  19. return false
  20. }
  21. })
  22. }

经过以上一系列操作,最终可以得出,该音频文件是个 aac 类型的文件。

总结

不只是音频文件,所有文件类型都可以通过 Magic Number 来判断文件类型,原理都是一样的,只要能找到这个数字,就能判断类型,不管有无文件后缀。