前言
在日常项目,文件上传是一个非常常见的需求,但是文件类型的判断确实一个大难题。在我使用element-ui里面的upload进行判断时,官方的例子:
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
}
但是在我的使用过程中通过file.type也就是常说的MIME(多用途互联网邮件拓展类型)进行判断的话,会出现以下问题:
1.个别文件在mac和windows上传获取到的MIME类型不同。
由此我们开始使用了文件名称的后缀去做判断: 、
beforeAvatarUpload(file) {
const splitList = file.name.split('.')
const type = splitList[splitList.length - 1].toLocaleLowerCase()
const typeList = ['rar', 'zip', 'doc', 'docx', 'pdf', 'jpg', 'png']
const isTrueFileType = typeList.some(item => item === type)
if (!isTrueFileType) {
this.$message.error('文件格式不对')
}
}
使用文件后缀做判断同样有个问题,当一个文件的后缀名称进行改动,则无法上传,无法判断文件的真实类型。 最终我们使用了读取文件二进制的数据来进行文件类型判断。
js部分代码
function readBuffer(file, start, end) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsArrayBuffer(file.slice(start, end));
});
}
- file对象是继承blob对象的,所以file.slice(start, end)可以直接获取file对象中从start到end的字节。
- FileReader.readAsArrayBuffer是FileReader提供的内置方法, 异步按字节读取文件内容,结果用ArrayBuffer对象表示。
function check(normalHeaders) {
return (buffers) => {
return normalHeaders.every((header, index) => {
return header == buffers[index];
});
};
}
const isPNG = check([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
check方法主要是对比当前传入文件buffers和基础normalHeaders,运用的函数的柯里化,返回一个比较函数。
总结
以上两个方法就可以简单实现通过blob真实对比一个文件的真实类型,其原理还是通过获取文件的二进制数据时,头部的字节保持不变,进而对比文件的类型。使用方法类似:
const handleChange = async event => {
let file = event.target.files[0];
let buffer = await readBuffer(file, 0, 8);
let uint8Array = new Uint8Array(buffer);
isPNG(uint8Array)
};