图片校验

文件校验

图片校验

文件后缀名校验

1
2
3
4
5
6
7
8
/**
* 获取文件后缀名
* @param { File } file
* @returns { String } 文件名
*/
function getFileExtension(file) {
return file.name.match(/(?<=\.)\w+$/)?.[0].toUpperCase() ?? '';
}

通过正则获取最后一个 ‘.‘ 后的字符,并转为大写字符。

二进制文件校验

将文件读取为二进制文件,并转十六进制字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 将blob转为16进制字符串
* @param { Blob } blob
* @returns { String }
*/
async function blobToHexString(blob) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => {
// 转为16进制,大写,2位一组补零
const hexString = reader.result.split('')
.map((char) => char.charCodeAt().toString(16).toUpperCase().padStart(2, '0'))
.join(' ');
resolve(hexString);
};
reader.readAsBinaryString(blob);
});
}

根据不同文件类型判断

  • PNG

    二进制开头为 89 50 4E 47 0D 0A 1A 0A

    PNG头部
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 判断是否为PNG
    * 二进制开头为 89 50 4E 47 0D 0A 1A 0A
    * @param { File } file 需要判断的文件
    * @returns { Boolean }
    */
    function isPNGBinary(file) {
    const PNG_RET = '89 50 4E 47 0D 0A 1A 0A';
    const ret = await blobToHexString(file.slice(0, 8));
    return ret === PNG_RET;
    }
  • JPG / JPEG

    二进制开头为 FF D8,结尾为 FF D9

    JPG头部/尾部

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 判断是否为JPG
    * 二进制开头为 FF D8
    * 二进制结尾为 FF D9
    * @param { File } file 需要判断的文件
    * @returns { Boolean }
    */
    function isJPGBinary(file) {
    const JPG_RET_START = 'FF D8';
    const JPG_RET_END = 'FF D9';
    const startRet = await blobToHexString(file.slice(0, 2));
    const endRet = await blobToHexString(file.slice(-2));
    return startRet === JPG_RET_START && endRet === JPG_RET_END;
    }
  • GIF

    二进制开头为 47 49 46 38 39 61 或 47 49 46 38 37 61

    GIF头部

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 判断是否为GIF
    * 二进制开头为 47 49 46 38 39 61 或 47 49 46 38 37 61
    * @param { File } file 需要判断的文件
    * @returns { Boolean }
    */
    function isGIFBinary(file) {
    const GIF_RET_1 = '47 49 46 38 39 61';
    const GIF_RET_2 = '47 49 46 38 37 61';
    const ret = await blobToHexString(file.slice(0, 6));
    return ret === GIF_RET_1 || ret === GIF_RET_2;
    }

图片宽高校验

在二进制文件中,根据不同类型的图片类型读取图片的宽高。

  • PNG:宽,第6、7字节;高,第8、9字节
  • JPG / JPEG:宽,第165、166字节;高,第163、164字节
  • GIF:宽,第6、7字节;高,8、9字节;需反转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const REACT_GET_CONFIG = {
[FILE_TYPES.JPEG]: { widthOffset: [165, 167], heightOffset: [163, 165], reverse: false },
[FILE_TYPES.JPG]: { widthOffset: [165, 167], heightOffset: [163, 165], reverse: false },
[FILE_TYPES.PNG]: { widthOffset: [18, 20], heightOffset: [22, 24], reverse: false },
[FILE_TYPES.GIF]: { widthOffset: [6, 8], heightOffset: [8, 10], reverse: true },
};

/**
* 根据偏移量,在二进制文件中读取像素大小
* @param { File } file 文件
* @param { Number } offsetBegin 偏移起始位置
* @param { Number } offsetEnd 偏移结束位置
* @param { Boolean } reverse 是否反转
*/
async function getPixel(file, offsetBegin, offsetEnd, reverse = false) {
let hexPixel = await blobToHexString(file.slice(offsetBegin, offsetEnd));
if (reverse) {
// 比如gif 的宽,6和7 是反着排的 大小端存储
// 比如6位仕89,7位仕02, gif就是 0289 而不是8920的值
hexPixel = hexPixel.split(' ').reverse().join(' ');
}
return parseInt(hexPixel.replace(' ', ''), 16);
}

/**
* 获取图片宽高
* @param { File } file
*/
async function getReact(file) {
// 根据文件后缀选择相应的偏移量
const { widthOffset, heightOffset, reverse } = REACT_GET_CONFIG[getFileExtension(file)];
const width = await getPixel(file, ...widthOffset, reverse);
const height = await getPixel(file, ...heightOffset, reverse);
return { width, height };
}

文件大小校验

1
2
3
4
5
6
7
8
/**
* 获取文件大小
* @param { File } file
* @returns { String }
*/
function getFileSize(file) {
return file.size;
}
0%