preface

This article is short, about 5 minutes, and tends to be practical rather than analytical.

background

There is an upload function in a small project. Because it is small, there is no verification on the back end this time. Check all to the front end, test a case is: change the suffix of the file can bypass upload. Out of confidence in Antd, I gave QA a pat on the chest to say yes. Results found that in fact not, in order not to be disS, tried to complete this function.

why

After the file type is changed, the browser changes the corresponding file.type as well. File. type in the following code can be determined by the suffix at the end of the file.

/ / the official 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;
}

Copy the code

The solution

The core implementation is to read the file through binary and verify the first 4 bits of the real file content in hexadecimal. The files that are allowed to be uploaded in this code are of XLSX type. The binary headers of other files can be found by themselves, such as the file type corresponding to the binary header bytes.

    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)];
                    // Just the first four digits of the file are enough
                    buffer = buffer.splice(0.4);
                    buffer.forEach((num, i, arr) = > {
                        arr[i] = num.toString(16).padStart(2.'0');
                    });
                    // 504b0304 is the XLSX header
                    resolve(buffer.join(' ') = = ='504b0304');
                } catch (e) {
                    // Error reading file header Default is not a valid file typereject(); }}; }); }Copy the code

Next up is the beforeUpload of the Antd Upload component, with minor modifications to the official demo

    beforeUpload: (file) = > {
        return new Promise(async (resolve, reject) => {
            const isExcel = await getFileMimeType(file); // Call the above code
            if(! isExcel) { message.error('Upload failed! Only files of type XLSX are supported.);
                reject();
            }
            const isLt10M = file.size / 1024 / 1024 < 10;
            if(! isLt10M) { message.error('Upload file cannot exceed 10MB! ');
                reject();
            }
            resolve();
        });
    }
Copy the code

Pay attention to the point

  • ReadAsArrayBuffer is asynchronous, so individuals are converted into promises for synchronous writing
  • In the beginning beforeUpload is written directly like this:
beforeUpload: async (file) => {
        const isExcel = await getFileMimeType(file); // Call the above code
        if(! isExcel) { message.error('Upload failed! Only files of type XLSX are supported.);
            return false;
        }
        const isLt10M = file.size / 1024 / 1024 < 10;
        if(! isLt10M) { message.error('Upload file cannot exceed 10MB! ');
            return false;
        }
        return true;
}
Copy the code

Files will always upload successfully with this writing, so modify the beforeUpload to return a Promise.