0
点赞
收藏
分享

微信扫一扫

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


这是我的专栏​​《JS真好玩》​​,将教你用JS实现一些有趣的东西。JS可以直接在浏览器运行,也可以在Node中运行,你可以跟我学习用JS做好玩儿的事情。感谢大家关注~文章求赞噢!祝大家早日变成一名「前端极客」!

欢迎阅读:本专栏最受欢迎文章​​《用JS找到哪个小坏蛋给我连点2次赞》​​。

背景

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

关于图片存储

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

但文件名后缀不是决定文件类型的(所以人类可以任意修改后缀,没有限制)。文件中,通常有 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 theASCII​​transfer mode​​ instead of thebinarymode.

上面提到的"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);

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

写在最后

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了​​《联机桌游合集》​​​,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费无广告。还独立开发了​​《合成大西瓜重制版》​​​。还开发了​​《Dice Crush》​​​参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:​​《教你做小游戏》​​​、​​《极致用户体验》​​。


举报

相关推荐

0 条评论