Antd Upload 上傳組件校驗文件真實類型

前言

本文篇幅較短,用時約爲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();
        });
    }
複製代碼

注意點

  • readAsArrayBuffer是異步的,所以我的轉換成了Promise 用於同步寫法
  • 一開始beforeUpload中是直接這樣寫的:
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。異步

相關文章
相關標籤/搜索