遇到一个需求:假定有一个图片文件,真实的类型为jpg,而有人偷懒把jpg直接复制一张,存为同名的png文件,这样在as3读取文件时不会遇到问题,但手机c++在读取文件时却遇到问题了 - -!
现在就需要写一个程序,遍历所有文件夹下的文件,查找文件格式“不正常”的文件。我们的资源主要是gif、png、jpg,最开始,我到网上找到一篇文章:根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,对与通过后缀名获得的文件类型进行比较。
var fd = fs.openSync(new_file_path, 'r'); var buffer = new Buffer(8);var mineType = mime.lookup(new_file_path); var fileType = mime.extension(mineType);
fs.readSync(fd, buffer, 0, 8, 0); var newBuf = buffer.slice(0, 4); var head_1 = newBuf[0].toString(16); var head_2 = newBuf[1].toString(16); var head_3 = newBuf[2].toString(16); var head_4 = newBuf[3].toString(16); var head_iden = head_1 + head_2;
var tempFileType = FILE_TYPE_CONFIG[head_iden]; if (!tempFileType) { head_iden += head_3;
tempFileType = FILE_TYPE_CONFIG[head_iden];
if (!tempFileType) { var msg = "Unknow fileType " + new_file_path + '-' + fileType; showLog(msg); continue; } }
if (tempFileType != fileType) { var msg = "Error fileType" + new_file_path + '-' + fileType + '|' + tempFileType + '--正确的图像文件格式'; showLog(msg);
g_errorFileTypArr.push(msg); }
后来搜索node image相关的信息时,找到这篇文章:node.js module ranking>> (images)
然后筛选到一个模块“node-imageinfo”,写了一个例子进行测试(故意把jpg文件直接修改后缀名为png):
它的源码,有兴趣可以研究一下:
function readUInt32(buffer, offset, bigEndian) { if (buffer.readUInt32) { return buffer.readUInt32(offset, bigEndian); }var value; if (bigEndian) { if (buffer.readUInt32BE) { return buffer.readUInt32BE(offset); } value = (buffer[offset] << 24) + (buffer[offset+1] << 16) + (buffer[offset+2] << 8) + buffer[offset+3]; } else { if (buffer.readUInt32LE) { return buffer.readUInt32LE(offset); } value = buffer[offset] + (buffer[offset+1] << 8) + (buffer[offset+2] << 16) + (buffer[offset+3] << 24); } return value; }
function readUInt16(buffer, offset, bigEndian) { if (buffer.readUInt16) { return buffer.readUInt16(offset, bigEndian); }
var value; if (bigEndian) { if (buffer.readUInt16BE) { return buffer.readUInt16BE(offset); } value = (buffer[offset] << 8) + buffer[offset+1]; } else { if (buffer.readUInt16LE) { return buffer.readUInt16LE(offset); } value = buffer[offset] + (buffer[offset+1] << 8); } return value; }
function readBit(buffer, offset, bitOffset) { if (bitOffset > 7) { offset += Math.floor(bitOffset / 8); bitOffset = bitOffset % 8; }
var b = buffer[offset]; if (bitOffset < 7) { b >>>= (7 - bitOffset); }
var val = b & 0x01; return val; }
function readBits(buffer, offset, bitOffset, bitLen, signed) { var val = 0; var neg = false; if (signed) { if (readBit(buffer, offset, bitOffset) > 0) { neg = true; } bitLen--; bitOffset++; }
var bytes = []; for (var i = 0; i < bitLen; i++) { var b = readBit(buffer, offset, bitOffset + i); if (i>0 && (bitLen - i) % 8 == 0) { bytes.push(val); val = 0; } val <<= 1; val |= b; } bytes.push(val);
val = new Buffer(bytes); val.negative = neg?true:false; return val; }
function imageInfoPng(buffer) { var imageHeader = [0x49, 0x48, 0x44, 0x52], pos = 12;
if (!checkSig(buffer, pos, imageHeader)) { return false; }
pos += 4; return { type: 'image', format: 'PNG', mimeType: 'image/png', width: readUInt32(buffer, pos, true), height: readUInt32(buffer, pos+4, true), }; }
function imageInfoJpg(buffer) { var pos = 2, len = buffer.length, sizeSig = [0xff, [0xc0, 0xc2]];
while (pos < len) { if (checkSig(buffer, pos, sizeSig)) { pos += 5; return { type: 'image', format: 'JPG', mimeType: 'image/jpeg', width: readUInt16(buffer, pos+2, true), height: readUInt16(buffer, pos, true), }; }
pos += 2; var size = readUInt16(buffer, pos, true); pos += size; } }
function imageInfoGif(buffer) { var pos = 6;
return { type: 'image', format: 'GIF', mimeType: 'image/gif', width: readUInt16(buffer, pos, false), height: readUInt16(buffer, pos+2, false), }; }
function imageInfoSwf(buffer) { var pos = 8, bitPos = 0, val;
if (buffer[0] === 0x43) { try { // If you have zlib available ( npm install zlib ) then we can read compressed flash files buffer = require('zlib').inflate(buffer.slice(8, 100)); pos = 0; } catch (ex) { // Can't get width/height of compressed flash files... yet (need zlib) return { type: 'flash', format: 'SWF', mimeType: 'application/x-shockwave-flash', width: null, height: null, } } }
var numBits = readBits(buffer, pos, bitPos, 5)[0]; bitPos += 5; val = readBits(buffer, pos, bitPos, numBits, true); var xMin = (numBits > 9 ? readUInt16(val, 0, true) : val[0]) * (val.negative ? -1 : 1); bitPos += numBits;
val = readBits(buffer, pos, bitPos, numBits, true); var xMax = (numBits > 9 ? readUInt16(val, 0, true) : val[0]) * (val.negative ? -1 : 1); bitPos += numBits;
val = readBits(buffer, pos, bitPos, numBits, true); var yMin = (numBits > 9 ? readUInt16(val, 0, true) : val[0]) * (val.negative ? -1 : 1); bitPos += numBits;
val = readBits(buffer, pos, bitPos, numBits, true); var yMax = (numBits > 9 ? readUInt16(val, 0, true) : val[0]) * (val.negative ? -1 : 1);
return { type: 'flash', format: 'SWF', mimeType: 'application/x-shockwave-flash', width: Math.ceil((xMax - xMin) / 20), height: Math.ceil((yMax - yMin) / 20), }; }
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; }
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; };