[JS真好玩] 图片文件没后缀,怎么判断图片类型?

在处理无后缀的图片时,通过Magic Number来判断文件类型。本文介绍了两种NodeJS方案:imageinfo库,仅支持png、jpg、gif、swf,通过匹配文件头部标识确定类型;file-type库功能更全面,支持多种类型,同时适用于NodeJS和浏览器环境。

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

背景

最近我在处理图片,遇到个问题:从网上下载的图片,都没有后缀标明文件类型。我希望写代码识别他们的后缀,并自动改名字。

关于图片存储

文件用后缀标明文件类型,是给计算机的「文件管理器」和人类看的。「文件管理器」可以直接读取文件名,知道用什么软件打开文件,「人类」也可以直观的从文件名中知道类型。

但文件名后缀不是决定文件类型的(所以人类可以任意修改后缀,没有限制)。文件中,通常有 Magic Number 标识文件类型。这个 Magic Number 通常位于文件的头部。一个文件其实就是一个长的二进制序列,头部就是这些二进制序列的前几个字节。

以下来自维基百科对 Magic Number 的一些案例:

  • GIF image files have the ASCII code for “GIF89a” (47 49 46 38 39 61) or “GIF87a” (47 49 46 38 37 61)
  • JPEG image files begin with FF D8 and end with FF D9. JPEG/JFIF files contain the ASCII code for “JFIF” (4A 46 49 46) as a null terminated string. JPEG/Exif files contain the ASCII code for “Exif” (45 78 69 66) also as a null terminated string, followed by more metadata about the file.
  • PNG image files begin with an 8-byte signature which identifies the file as a PNG file and allows detection of common file transfer problems: \211 P N G \r \n \032 \n (89 50 4E 47 0D 0A 1A 0A). That signature contains various newline characters to permit detecting unwarranted automated newline conversions, such as transferring the file using FTP with the ASCII transfer mode instead of the binary mode.

上面提到的"GIF89a" (47 49 46 38 39 61),引号里是ASCII,括号里是16进制,你可以去我写的工具里轻松转换:tool.hullqin.cn/byte-parser… ,工具介绍文章在《手写解析uin8数组的工具:解析二进制字节,太快太方便了!》

方案一:imageinfo

安装

npm i imageinfo 

在NodeJS中使用

const imageinfo = require('imageinfo');
const img = fs.readFileSync('图片文件路径');
const info = imageinfo(img);
console.log(info);
console.log(info.format);
console.log("Data is type:", info.mimeType);
console.log("Size:", img.length, "bytes");
console.log("Dimensions:", info.width, "x", info.height); 

得到info后,使用info.format.toLowerCase()就能获得文件后缀。

这个库比较简洁,只能识别png jpg gif swf。如果文件未识别到图片类型,info就是false了,要注意特殊处理这种情况。

源码

imageinfo源码非常简单:

module.exports = function imageInfo(buffer, path) {
 var pngSig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
 var jpgSig = [0xff, 0xd8, 0xff];
 var gifSig = [0x47, 0x49, 0x46, 0x38, [0x37, 0x39], 0x61];
 var swfSig = [[0x46, 0x43], 0x57, 0x53];

 if (checkSig(buffer, 0, pngSig)) return imageInfoPng(buffer);
 if (checkSig(buffer, 0, jpgSig)) return imageInfoJpg(buffer);
 if (checkSig(buffer, 0, gifSig)) return imageInfoGif(buffer);
 if (checkSig(buffer, 0, swfSig)) return imageInfoSwf(buffer);

 return false;
}; 

可以看到它定义了一些头部标识,然后通过checkSig去匹配。我们再看看checkSig

function checkSig(buffer, offset, sig) {
 var len = sig.length;
 for (var i = 0; i < len; i++) {var b = buffer[i+offset], s = sig[i], m = false;if ('number' == typeof s) { m = s === b;}else { for (var k in s) {var o = s[k];if (o === b) { m = true;} }}if (!m) { return false;}
 }

 return true;
} 

也很简单,就是拿文件头部几个字节和之前定义的头部标识进行逐个匹配,全匹配成功,返回true,否则就返回false。

方案二:file-type

这个库不像imagetype走简洁路线,file-type功能更强大,能识别的类型更多。同时支持NodeJS环境和浏览器环境。

官方介绍如下:

The file type is detected by checking the magic number of the buffer.

This package is for detecting binary-based file formats, not text-based formats like .txt, .csv, .svg, etc.

意思是,这个库通过检查文件二进制序列的 magic number 来判断文件类型。不支持 txt csv svg这些文本类型文件的判断。

安装

npm i file-type 

使用

NodeJS使用:

import {fileTypeFromFile} from 'file-type';

console.log(await fileTypeFromFile('Unicorn.png'));
//=> {ext: 'png', mime: 'image/png'} 

浏览器使用:

import {fileTypeFromStream} from 'file-type';

const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';

const response = await fetch(url);
const fileType = await fileTypeFromStream(response.body);

console.log(fileType);
//=> {ext: 'jpg', mime: 'image/jpeg'} 

更多API,可阅读官方文档: file-type

写在最后

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》《极致用户体验》。最后,整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值