如何在上传至网页前检测文件是否来自Zip压缩包?
解决方案:检测拖拽文件是否为压缩包内未解压的损坏文件
浏览器的拖拽API出于安全限制,不会暴露文件的来源路径或是否来自压缩包这类信息,所以没法直接从dataTransfer里拿到标记。但可以通过**验证文件的实际内容(魔数/文件签名)**来判断文件是否有效,间接识别出从压缩包拖拽出的未解压损坏文件。
核心思路
从压缩包内拖拽出的文件,浏览器实际获取到的是该文件在压缩包中的原始压缩数据流,而非解压后的正常文件内容。这类文件的开头字节(魔数)会和对应格式的正常文件不符,我们可以通过读取文件头部的几个字节来验证。
TypeScript 实现示例
1. 读取文件头部字节的工具函数
// 读取文件前N个字节,用于验证魔数 async function getFileHeader(file: File, byteCount: number): Promise<Uint8Array> { const fileSlice = file.slice(0, byteCount); const buffer = await fileSlice.arrayBuffer(); return new Uint8Array(buffer); }
2. 针对不同文件类型的验证逻辑
这里以常见的PNG、JPG、文本文件为例:
async function isValidUploadFile(file: File): Promise<boolean> { // 验证PNG文件:魔数为 0x89 0x50 0x4E 0x47 if (file.type === 'image/png') { const header = await getFileHeader(file, 4); return header[0] === 0x89 && header[1] === 0x50 && header[2] === 0x4E && header[3] === 0x47; } // 验证JPG文件:魔数为 0xFF 0xD8 0xFF if (file.type === 'image/jpeg') { const header = await getFileHeader(file, 3); return header[0] === 0xFF && header[1] === 0xD8 && header[2] === 0xFF; } // 验证文本文件:尝试读取部分内容,判断是否为乱码(压缩后的二进制数据转字符串会是乱码) if (file.type.startsWith('text/')) { const textSlice = await file.slice(0, 100).text(); // 简单判断是否包含大量不可打印字符 const unprintableCharRatio = (textSlice.match(/[\x00-\x1F\x7F-\xFF]/g)?.length || 0) / textSlice.length; return unprintableCharRatio < 0.2; } // 其他文件类型可参考对应格式的魔数自行扩展 return true; }
3. 在drop事件中使用验证逻辑
const dropArea = document.getElementById('upload-area') as HTMLDivElement; dropArea.addEventListener('drop', async (e) => { e.preventDefault(); const files = Array.from(e.dataTransfer.files); for (const file of files) { const isFileValid = await isValidUploadFile(file); if (!isFileValid) { alert(`文件「${file.name}」可能来自未解压的压缩包,请先解压后再上传`); continue; } // 执行正常的上传流程 await uploadFile(file); } }); // 示例上传函数 async function uploadFile(file: File) { // 你的上传逻辑代码 }
补充说明
- 魔数是每种文件类型的固定标识,几乎所有标准文件格式都有对应的魔数(比如PDF的魔数是
%PDF-,Zip包本身的魔数是PK\x03\x04),可以根据你支持的上传文件类型逐一扩展验证逻辑。 - 对于没有明确魔数的自定义格式,可以尝试读取文件内容并尝试解析,若解析失败则判定为无效文件。
- 这种方法无法100%覆盖所有场景,但能解决绝大多数从压缩包拖拽未解压文件的问题。
内容的提问来源于stack exchange,提问作者Papageno
相关产品推荐
相关产品推荐

