本文篇幅较短,用时约为5分钟,偏向实用性,不讲分析。javascript
有个小项目中有一个上传功能,由于小,因此本次后端是没有作任何校验的。校验全交给前端,测试有个case是:改了后缀名的的文件是否能绕过上传。本着对Antd的信任,我对QA拍胸口表示能够。结果后面发现其实不行,为了避免被diss,千方百计完成了这个功能。前端
修改文件类型后,浏览器会将其对应的file.type也会随着更改。以下代码中的file.type ,彻底能够改成根据文件最后的后缀名进行判断。java
// 官方demo
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}
复制代码
核心实现是经过二进制读取文件,校验真实文件内容的前4位的16进制。本代码中容许上传的文件是xlsx类型,其他文件二进制头能够自行查找,好比二进制头字节对应的文件类型。后端
getFileMimeType: (file) => {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
return new Promise((resolve, reject) => {
reader.onload = (event) => {
try {
let buffer = [...Buffer.from(event.target.result)];
// 仅要文件的前四位就够了
buffer = buffer.splice(0, 4);
buffer.forEach((num, i, arr) => {
arr[i] = num.toString(16).padStart(2, '0');
});
// 504b0304 是 xlsx 的文件头
resolve(buffer.join('') === '504b0304');
} catch (e) {
// 读取文件头出错 默认不是合法文件类型
reject();
}
};
});
}
复制代码
接下来是Antd upload组件的beforeUpload,稍微修改下官方demo便可浏览器
beforeUpload: (file) => {
return new Promise(async (resolve, reject) => {
const isExcel = await getFileMimeType(file); // 调用上面代码
if (!isExcel) {
message.error('上传失败!仅支持文件类型为xlsx的文件');
reject();
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('上传文件不能超过 10MB!');
reject();
}
resolve();
});
}
复制代码
beforeUpload: async (file) => {
const isExcel = await getFileMimeType(file); // 调用上面代码
if (!isExcel) {
message.error('上传失败!仅支持文件类型为xlsx的文件');
return false;
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('上传文件不能超过 10MB!');
return false;
}
return true;
}
复制代码
用这种写法文件都会上传成功,所以改造了beforeUpload,让其返回Promise。异步