前言

在公司项目中,上传应用 Logo 时,前端限制只能上传 PNG 格式的文件。然而,有些用户可能会通过手动修改文件后缀名的方式上传非 PNG 格式的文件(如 JPG)。这会导致客户端在打包时失败。因此,我们需要一种方法来验证文件的真实扩展名。

解决方案

我们可以通过读取文件的前几个字节来获取文件的真实扩展名。以下是实现步骤:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* 获取文件的真实扩展名
* @param {File} file - 要检查的文件对象
* @returns {Promise<string>} 返回文件扩展名的Promise
*/
export const getRealFileExtension = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();

reader.onloadend = function (_e) {
const arr = new Uint8Array(reader.result as ArrayBuffer);
let header = '';
for (let i = 0; i < arr.length; i++) {
header += arr[i].toString(16).padStart(2, '0');
}

// 常见文件类型的文件头标识
const fileSignatures = {
jpg: ['ffd8ff'],
png: ['89504e47'],
gif: ['47494638'],
bmp: ['424d'],
pdf: ['25504446'],
zip: ['504b0304'],
rar: ['52617221'],
// 添加更多文件类型和对应的文件头标识
};

for (const [extension, signatures] of Object.entries(fileSignatures)) {
for (const signature of signatures) {
if (header.startsWith(signature)) {
resolve(extension);
return;
}
}
}

resolve('unknown'); // 未匹配到已知文件类型
};

reader.onerror = function () {
reject('读取文件失败');
};

// 读取文件的前4个字节
const blob = file.slice(0, 4);
reader.readAsArrayBuffer(blob);
});
}

在上传文件时,使用以下代码进行验证:

1
2
3
4
5
6
7
const fileType = await fileUtil.getRealFileExtension(data.file.file);
const fileExtension = data.file.name.split('.').pop().toLowerCase();
console.log('fileType: ' + fileType, 'fileExtension: ' + fileExtension)
if (fileType !== fileExtension) {
message.error('文件后缀与真实类型不匹配,请上传正确的文件');
return false;
}

参考

  • 这个网址可以上传文件来查看真实后缀: Exif Tool