本文篇幅較短,用時約爲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。異步