From 7e8bef0cb9cc516d2660eb8ff235401b8fdd7426 Mon Sep 17 00:00:00 2001 From: xiebing Date: Fri, 19 Dec 2025 14:46:37 +0800 Subject: [PATCH] =?UTF-8?q?feat=20:=20=E4=B8=80=E4=BD=93=E6=9C=BA=E5=AE=9A?= =?UTF-8?q?=E4=BD=8D=E7=9B=B4=E5=88=B0=E6=88=90=E5=8A=9F,=E5=85=B6?= =?UTF-8?q?=E4=BB=96=E7=8E=AF=E5=A2=83=E5=BE=AA=E7=8E=AF2=E5=88=86?= =?UTF-8?q?=E9=92=9F=E5=AE=9A=E4=BD=8D,=20=E4=B8=8A=E4=BC=A0=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=96=87=E4=BB=B6=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 12 +- packageA/pages/personalInfo/personalInfo.vue | 16 +- pages/chat/components/ai-paging.vue | 70 +-- pages/index/components/index-refactor.vue | 1 - pages/index/components/index-two.vue | 1 - pages/search/search.vue | 1 - stores/useLocationStore.js | 24 +- utils/fileValidator.js | 481 ++++++++++--------- 8 files changed, 343 insertions(+), 263 deletions(-) diff --git a/App.vue b/App.vue index 620f282..4054bd3 100644 --- a/App.vue +++ b/App.vue @@ -24,6 +24,7 @@ onLaunch((options) => { getUserInfo(); useUserStore().changMiniProgramAppStatus(false); useUserStore().changMachineEnv(false); + useLocationStore().getLocationLoop()//循环获取定位 return; } if (isY9MachineType()) { @@ -32,7 +33,14 @@ onLaunch((options) => { useUserStore().logOutApp(); useUserStore().changMiniProgramAppStatus(true); useUserStore().changMachineEnv(true); - useLocationStore().getLocation(); + useLocationStore().getLocation() + .then(({longitude,latitude})=>{ + console.log(`✅一体机获取定位成功:lng:${longitude},lat${latitude}`) + }) + .catch(err=>{ + console.log('❌一体机获取定位失败,30s后尝试重新获取') + useLocationStore().getLocation() + }) uQRListen = new IncreaseRevie(); inactivityManager = new GlobalInactivityManager(handleInactivity, 60 * 1000); inactivityManager.start(); @@ -40,6 +48,7 @@ onLaunch((options) => { } // 正式上线去除此方法 console.warn('浏览器环境'); + useLocationStore().getLocationLoop()//循环获取定位 useUserStore().changMiniProgramAppStatus(true); useUserStore().changMachineEnv(false); useUserStore().initSeesionId(); //更新 @@ -57,6 +66,7 @@ onLaunch((options) => { onMounted(() => {}); + onShow(() => { console.log('App Show'); }); diff --git a/packageA/pages/personalInfo/personalInfo.vue b/packageA/pages/personalInfo/personalInfo.vue index 952b2d6..64911c4 100644 --- a/packageA/pages/personalInfo/personalInfo.vue +++ b/packageA/pages/personalInfo/personalInfo.vue @@ -82,6 +82,7 @@ const { userInfo } = storeToRefs(useUserStore()); const { getUserResume } = useUserStore(); const { dictLabel, oneDictData } = useDictStore(); const openSelectPopup = inject('openSelectPopup'); +import {FileValidator} from "@/utils/fileValidator" //文件校验 const percent = ref('0%'); const state = reactive({ @@ -278,8 +279,16 @@ function selectAvatar() { sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], count: 1, - success: ({ tempFilePaths, tempFiles }) => { - $api.uploadFile(tempFilePaths[0], true) + success: async(res) => { + const tempFilePaths = res.tempFilePaths; + const file = res.tempFiles[0]; + + const imageValidator = new FileValidator() + + try { + await imageValidator.validate(file) + + $api.uploadFile(tempFilePaths[0], true) .then((res) => { res = JSON.parse(res); if (res.msg) fromValue.avatar = res.msg; @@ -287,6 +296,9 @@ function selectAvatar() { .catch((err) => { $api.msg('上传失败'); }); + } catch (error) { + $api.msg(error); + } }, fail: (error) => {}, }); diff --git a/pages/chat/components/ai-paging.vue b/pages/chat/components/ai-paging.vue index e4868e2..448e327 100644 --- a/pages/chat/components/ai-paging.vue +++ b/pages/chat/components/ai-paging.vue @@ -281,6 +281,8 @@ const { messages, isTyping, textInput, chatSessionID } = storeToRefs(useChatGrou import successIcon from '@/static/icon/success.png'; import useUserStore from '@/stores/useUserStore'; const { isMachineEnv } = storeToRefs(useUserStore()); + +import {FileValidator} from "@/utils/fileValidator" //文件校验 // hook // 语音识别 const { @@ -536,22 +538,29 @@ function uploadCamera(type = 'camera') { count: 1, //默认9 sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有 sourceType: [type], //从相册选择 - success: function (res) { + success: async (res)=> { const tempFilePaths = res.tempFilePaths; const file = res.tempFiles[0]; - // 继续上传 - $api.uploadFile(tempFilePaths[0], true).then((resData) => { - resData = JSON.parse(resData); - console.log(file.type,'++') - if (isImage(file.type)) { - filesList.value.push({ - url: resData.msg, - type: file.type, - name: file.name, - }); - textInput.value = state.uploadFileTips; - } - }); + + const imageValidator = new FileValidator() + try { + await imageValidator.validate(file) + + $api.uploadFile(tempFilePaths[0], true).then((resData) => { + resData = JSON.parse(resData); + console.log(file.type,'++') + if (isImage(file.type)) { + filesList.value.push({ + url: resData.msg, + type: file.type, + name: file.name, + }); + textInput.value = state.uploadFileTips; + } + }); + } catch (error) { + $api.msg(error) + } }, }); } @@ -560,25 +569,30 @@ function getUploadFile(type = 'camera') { if (VerifyNumberFiles()) return; uni.chooseFile({ count: 1, - success: (res) => { + success: async(res) => { const tempFilePaths = res.tempFilePaths; const file = res.tempFiles[0]; const allowedTypes = config.allowedFileTypes || []; const size = $api.formatFileSize(file.size); - if (!allowedTypes.includes(file.type)) { - return $api.msg('仅支持 txt md word pdf ppt csv excel 格式类型'); - } - // 继续上传 - $api.uploadFile(tempFilePaths[0], true).then((resData) => { - resData = JSON.parse(resData); - filesList.value.push({ - url: resData.msg, - type: file.type, - name: file.name, - size: size, + + const imageValidator = new FileValidator({allowedExtensions:config.allowedFileTypes}) + + try{ + await imageValidator.validate(file) + + $api.uploadFile(tempFilePaths[0], true).then((resData) => { + resData = JSON.parse(resData); + filesList.value.push({ + url: resData.msg, + type: file.type, + name: file.name, + size: size, + }); + textInput.value = state.uploadFileTips; }); - textInput.value = state.uploadFileTips; - }); + }catch(error){ + $api.msg(error) + } }, }); } diff --git a/pages/index/components/index-refactor.vue b/pages/index/components/index-refactor.vue index 185c693..9bd0d5e 100644 --- a/pages/index/components/index-refactor.vue +++ b/pages/index/components/index-refactor.vue @@ -470,7 +470,6 @@ const { columnCount, columnSpace } = useColumnCount(() => { getJobRecommend('refresh'); nextTick(() => { waterfallsFlowRef.value?.refresh?.(); - useLocationStore().getLocation(); }); }); diff --git a/pages/index/components/index-two.vue b/pages/index/components/index-two.vue index 4ea08ea..14c80c0 100644 --- a/pages/index/components/index-two.vue +++ b/pages/index/components/index-two.vue @@ -129,7 +129,6 @@ const { columnCount, columnSpace } = useColumnCount(() => { pageSize.value = 10 * (columnCount.value - 1); nextTick(() => { waterfallsFlowRef.value?.refresh?.(); - useLocationStore().getLocation(); }); }); diff --git a/pages/search/search.vue b/pages/search/search.vue index 0b27ae2..75fdce9 100644 --- a/pages/search/search.vue +++ b/pages/search/search.vue @@ -151,7 +151,6 @@ const { columnCount, columnSpace } = useColumnCount(() => { pageSize.value = 10 * (columnCount.value - 1); nextTick(() => { waterfallsFlowRef.value?.refresh?.(); - useLocationStore().getLocation(); }); }); diff --git a/stores/useLocationStore.js b/stores/useLocationStore.js index ab7b2fe..b4d312d 100644 --- a/stores/useLocationStore.js +++ b/stores/useLocationStore.js @@ -11,13 +11,15 @@ import config from '../config'; const defalutLongLat = { longitude: 120.382665, - latitude: 36.066938 + latitude: 36.066938, } const useLocationStore = defineStore("location", () => { // 定义状态 const longitudeVal = ref(null) // 经度 const latitudeVal = ref(null) //纬度 + const timer = ref(null) + const count = ref(0) function getLocation() { // 获取经纬度两个平台 return new Promise((resole, reject) => { @@ -66,6 +68,21 @@ const useLocationStore = defineStore("location", () => { } }) } + function getLocationLoop(gap = 1000 * 60 * 2) { + console.log(`🔄开始循环获取定位,间隔:${Math.floor(gap/1000)}秒`) + const run = () => { + count.value++ + console.log(`📍第${count.value}次获取定位`) + getLocation() + } + run() + timer.value = setInterval(run,gap); + } + + function clearGetLocationLoop(params) { + clearInterval(timer.value) + timer.value = null + } function longitude() { return longitudeVal.value @@ -78,9 +95,10 @@ const useLocationStore = defineStore("location", () => { // 导入 return { getLocation, + getLocationLoop, + clearGetLocationLoop, longitudeVal, - latitudeVal - + latitudeVal, } }, { unistorage: true, diff --git a/utils/fileValidator.js b/utils/fileValidator.js index 305f176..8797e87 100644 --- a/utils/fileValidator.js +++ b/utils/fileValidator.js @@ -7,248 +7,277 @@ // 1. 预定义:已知文件类型的魔数 (Signature Database) // ========================================== const KNOWN_SIGNATURES = { - // === 图片 === - png: '89504E470D0A1A0A', - jpg: 'FFD8FF', - jpeg: 'FFD8FF', - gif: '47494638', - webp: '52494646', // RIFF Header + // === 图片 === + png: '89504E470D0A1A0A', + jpg: 'FFD8FF', + jpeg: 'FFD8FF', + gif: '47494638', + webp: '52494646', // RIFF Header - // === 文档 (Office 新版 - ZIP 格式) === - docx: '504B0304', - xlsx: '504B0304', - pptx: '504B0304', + // === 文档 (Office 新版 - ZIP 格式) === + docx: '504B0304', + xlsx: '504B0304', + pptx: '504B0304', - // === 文档 (Office 旧版 - OLECF 格式) === - doc: 'D0CF11E0', - xls: 'D0CF11E0', - ppt: 'D0CF11E0', + // === 文档 (Office 旧版 - OLECF 格式) === + doc: 'D0CF11E0', + xls: 'D0CF11E0', + ppt: 'D0CF11E0', - // === 其他 === - pdf: '25504446', + // === 其他 === + pdf: '25504446', - // === 纯文本 (无固定魔数,需特殊算法检测) === - txt: 'TYPE_TEXT', - csv: 'TYPE_TEXT', - md: 'TYPE_TEXT', - json: 'TYPE_TEXT', + // === 纯文本 (无固定魔数,需特殊算法检测) === + txt: 'TYPE_TEXT', + csv: 'TYPE_TEXT', + md: 'TYPE_TEXT', + json: 'TYPE_TEXT', }; // ========================================== // 2. 核心类定义 // ========================================== export class FileValidator { - /** - * 构造函数 - * @param {Object} options 配置项 - * @param {number} [options.maxSizeMB=10] 最大文件大小 (MB) - * @param {string[]} [options.allowedExtensions] 允许的扩展名列表 (如 ['jpg', 'png']),默认允许全部已知类型 - */ - version = '1.0.0'; - constructor(options = {}) { - // 配置大小 (默认 10MB) - this.maxSizeMB = options.maxSizeMB || 10; + /** + * 构造函数 + * @param {Object} options 配置项 + * @param {number} [options.maxSizeMB=10] 最大文件大小 (MB) + * @param {string[]} [options.allowedExtensions = ['png','jpg','jpeg','gif','webp','svg','bmp','ico','pdf','doc','docx','xls','xlsx','csv','ppt','pptx','txt','md']] 允许的扩展名列表 (如 ['jpg', 'png']),默认允许全部已知类型 + */ + version = '1.0.0'; + constructor(options = {}) { + // 配置大小 (默认 10MB) + this.maxSizeMB = options.maxSizeMB || 10; - // 配置允许的类型 - // 如果传入了 allowedExtensions,则只使用传入的;否则使用全部 KNOWN_SIGNATURES - if (options.allowedExtensions && Array.isArray(options.allowedExtensions)) { - this.allowedConfig = {}; - options.allowedExtensions.forEach((ext) => { - const key = ext.toLowerCase(); - if (KNOWN_SIGNATURES[key]) { - this.allowedConfig[key] = KNOWN_SIGNATURES[key]; - } else { - console.warn(`[FileValidator] 未知的文件类型: .${key},已忽略`); - } - }); + // 扩展名到 MIME 的映射(用于反向查找) + this.extToMime = { + png: 'image/png', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif', + webp: 'image/webp', + pdf: 'application/pdf', + txt: 'text/plain', + md: 'text/markdown', + json: 'application/json', + csv: 'text/csv', + doc: 'application/msword', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + xls: 'application/vnd.ms-excel', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ppt: 'application/vnd.ms-powerpoint', + pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + }; + + // 如果传入的是 MIME 类型,转换为扩展名 + let allowedExtensions = options.allowedExtensions || Object.keys(KNOWN_SIGNATURES); + + // 配置允许的类型 + this.allowedConfig = {}; + allowedExtensions.forEach((extOrMime) => { + const key = extOrMime.toLowerCase(); + + // 如果是 MIME 类型,尝试转换为扩展名 + let ext = key; + if (key.includes('/')) { + // 查找对应的扩展名 + for (const [e, mime] of Object.entries(this.extToMime)) { + if (mime === key) { + ext = e; + break; + } + } + } + + if (KNOWN_SIGNATURES[ext]) { + this.allowedConfig[ext] = KNOWN_SIGNATURES[ext]; + } else { + console.warn(`[FileValidator] 未知的文件类型: ${key},已忽略`); + } + }); + } + + /** + * 改进版:检查是否为有效的 UTF-8 文本 + */ + _isValidUTF8(buffer) { + try { + // fatal: true 会在遇到无效编码时抛出错误,而不是用 替换 + const decoder = new TextDecoder('utf-8', { + fatal: true, + }); + decoder.decode(buffer); + return true; + } catch (e) { + return false; + } + } + + /** + * 辅助:ArrayBuffer 转 Hex 字符串 + */ + _bufferToHex(buffer) { + return Array.prototype.map + .call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)) + .join('') + .toUpperCase(); + } + + /** + * 【新增】统计 CSV 行数(严谨版:忽略引号内的换行符) + * 性能:对于 10MB 文件,现代浏览器处理通常在 100ms 以内 + */ + _countCSVRows(buffer) { + const decoder = new TextDecoder('utf-8'); + const text = decoder.decode(buffer); + + let rowCount = 0; + let inQuote = false; + let len = text.length; + + // 遍历每一个字符 + for (let i = 0; i < len; i++) { + const char = text[i]; + + // 切换引号状态 + if (char === '"') { + inQuote = !inQuote; + } + // 只有在非引号状态下的换行符,才算作一行结束 + else if (char === '\n' && !inQuote) { + rowCount++; + } + } + + // 处理最后一行没有换行符的情况(且文件不为空) + if (len > 0 && text[len - 1] !== '\n') { + rowCount++; + } + + return rowCount; + } + + /** + * 【核心】:校验纯文本内容 + * 1. 检查是否包含乱码 (非 UTF-8) + * 2. 针对特定格式 (JSON) 进行语法解析 + */ + _validateTextContent(buffer, extension) { + // 1. 尝试解码为 UTF-8 + let contentStr = ''; + try { + const decoder = new TextDecoder('utf-8', { + fatal: true, + }); + contentStr = decoder.decode(buffer); + } catch (e) { + // 如果解码失败,说明包含非文本的二进制数据 + console.warn('UTF-8 解码失败', e); + return false; + } + + // 2. 检查是否存在过多的空字符 (二进制文件特征) + // 某些二进制文件可能勉强通过 UTF-8 解码,但会包含大量 \0 + if (contentStr.includes('\u0000')) { + return false; + } + + // 3. 针对特定后缀进行语法校验 (可选,更严格) + if (extension === 'json') { + try { + JSON.parse(contentStr); + } catch (e) { + console.warn('无效的 JSON 格式'); + return false; + } + } + + // 如果是 CSV,可以简单检查行数(可选) + // if (extension === 'csv') { ... } + + return true; + } + + /** + * 执行校验 + * @param {File} file 文件对象 + * @returns {Promise} + */ + validate(file) { + return new Promise((resolve, reject) => { + console.log('开始校验文件'); + // 1. 基础对象检查 + if (!file || !file.name) return reject('无效的文件对象'); + + // 2. 大小检查 + if (file.size > this.maxSizeMB * 1024 * 1024) { + return reject(`文件大小超出限制 (最大 ${this.maxSizeMB}MB)`); + } + + // 3. 后缀名检查 + const fileName = file.name.toLowerCase(); + const extension = fileName.substring(fileName.lastIndexOf('.') + 1); + + // 检查是否在配置的白名单中 + const expectedMagic = this.allowedConfig[extension]; + if (!expectedMagic) { + return reject(`不支持的文件格式: .${extension}`); + } + + // 4. 读取二进制头进行魔数校验 + const reader = new FileReader(); + + reader.onload = (e) => { + const buffer = e.target.result; + let isSafe = false; + + // 分支处理:纯文本 vs 二进制 + if (expectedMagic === 'TYPE_TEXT') { + if (this._validateTextContent(buffer, extension)) { + isSafe = true; + } else { + // 细化报错信息 + if (extension === 'json') { + return reject(`文件异常:不是有效的 JSON 文件`); + } + return reject(`文件异常:.${extension} 包含非法二进制内容或编码错误`); + } + + // 【新增】专门针对 CSV 的行数检查 + if (extension === 'csv' && this.csvMaxRows > 0) { + const rows = this._countCSVRows(buffer); + // 注意:这里通常把表头也算作 1 行,如果不算表头可以将 limit + 1 + if (rows > this.csvMaxRows) { + return reject(`CSV 行数超出限制 (当前 ${rows} 行,最大允许 ${this.csvMaxRows} 行)`); + } + } } else { - this.allowedConfig = { - ...KNOWN_SIGNATURES - }; - } - } + // 获取文件头 Hex (读取足够长的字节以覆盖最长的魔数,PNG需8字节) + const fileHeader = this._bufferToHex(buffer.slice(0, 8)); - /** - * 改进版:检查是否为有效的 UTF-8 文本 - */ - _isValidUTF8(buffer) { - try { - // fatal: true 会在遇到无效编码时抛出错误,而不是用 替换 - const decoder = new TextDecoder('utf-8', { - fatal: true - }); - decoder.decode(buffer); - return true; - } catch (e) { - return false; - } - } - - /** - * 辅助:ArrayBuffer 转 Hex 字符串 - */ - _bufferToHex(buffer) { - return Array.prototype.map - .call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)) - .join('') - .toUpperCase(); - } - - /** - * 【新增】统计 CSV 行数(严谨版:忽略引号内的换行符) - * 性能:对于 10MB 文件,现代浏览器处理通常在 100ms 以内 - */ - _countCSVRows(buffer) { - const decoder = new TextDecoder('utf-8'); - const text = decoder.decode(buffer); - - let rowCount = 0; - let inQuote = false; - let len = text.length; - - // 遍历每一个字符 - for (let i = 0; i < len; i++) { - const char = text[i]; - - // 切换引号状态 - if (char === '"') { - inQuote = !inQuote; - } - // 只有在非引号状态下的换行符,才算作一行结束 - else if (char === '\n' && !inQuote) { - rowCount++; - } + // 使用 startsWith 匹配 + if (fileHeader.startsWith(expectedMagic)) { + isSafe = true; + } else { + return reject(`文件可能已被篡改 (真实类型与 .${extension} 不符)`); + } } - // 处理最后一行没有换行符的情况(且文件不为空) - if (len > 0 && text[len - 1] !== '\n') { - rowCount++; - } + if (isSafe) resolve(true); + }; - return rowCount; - } + reader.onerror = () => reject('文件读取失败,无法校验'); - - /** - * 【核心】:校验纯文本内容 - * 1. 检查是否包含乱码 (非 UTF-8) - * 2. 针对特定格式 (JSON) 进行语法解析 - */ - _validateTextContent(buffer, extension) { - // 1. 尝试解码为 UTF-8 - let contentStr = ''; - try { - const decoder = new TextDecoder('utf-8', { - fatal: true - }); - contentStr = decoder.decode(buffer); - } catch (e) { - // 如果解码失败,说明包含非文本的二进制数据 - console.warn('UTF-8 解码失败', e); - return false; - } - - // 2. 检查是否存在过多的空字符 (二进制文件特征) - // 某些二进制文件可能勉强通过 UTF-8 解码,但会包含大量 \0 - if (contentStr.includes('\u0000')) { - return false; - } - - // 3. 针对特定后缀进行语法校验 (可选,更严格) - if (extension === 'json') { - try { - JSON.parse(contentStr); - } catch (e) { - console.warn('无效的 JSON 格式'); - return false; - } - } - - // 如果是 CSV,可以简单检查行数(可选) - // if (extension === 'csv') { ... } - - return true; - } - - /** - * 执行校验 - * @param {File} file 文件对象 - * @returns {Promise} - */ - validate(file) { - return new Promise((resolve, reject) => { - // 1. 基础对象检查 - if (!file || !file.name) return reject('无效的文件对象'); - - // 2. 大小检查 - if (file.size > this.maxSizeMB * 1024 * 1024) { - return reject(`文件大小超出限制 (最大 ${this.maxSizeMB}MB)`); - } - - // 3. 后缀名检查 - const fileName = file.name.toLowerCase(); - const extension = fileName.substring(fileName.lastIndexOf('.') + 1); - - // 检查是否在配置的白名单中 - const expectedMagic = this.allowedConfig[extension]; - if (!expectedMagic) { - return reject(`不支持的文件格式: .${extension}`); - } - - // 4. 读取二进制头进行魔数校验 - const reader = new FileReader(); - - reader.onload = (e) => { - const buffer = e.target.result; - let isSafe = false; - - // 分支处理:纯文本 vs 二进制 - if (expectedMagic === 'TYPE_TEXT') { - if (this._validateTextContent(buffer, extension)) { - isSafe = true; - } else { - // 细化报错信息 - if (extension === 'json') { - return reject(`文件异常:不是有效的 JSON 文件`); - } - return reject(`文件异常:.${extension} 包含非法二进制内容或编码错误`); - } - - // 【新增】专门针对 CSV 的行数检查 - if (extension === 'csv' && this.csvMaxRows > 0) { - const rows = this._countCSVRows(buffer); - // 注意:这里通常把表头也算作 1 行,如果不算表头可以将 limit + 1 - if (rows > this.csvMaxRows) { - return reject(`CSV 行数超出限制 (当前 ${rows} 行,最大允许 ${this.csvMaxRows} 行)`); - } - } - } else { - // 获取文件头 Hex (读取足够长的字节以覆盖最长的魔数,PNG需8字节) - const fileHeader = this._bufferToHex(buffer.slice(0, 8)); - - // 使用 startsWith 匹配 - if (fileHeader.startsWith(expectedMagic)) { - isSafe = true; - } else { - return reject(`文件可能已被篡改 (真实类型与 .${extension} 不符)`); - } - } - - if (isSafe) resolve(true); - }; - - reader.onerror = () => reject('文件读取失败,无法校验'); - - // 读取前 1KB 进行判断 - if (expectedMagic === 'TYPE_TEXT' && extension === 'json') { - // JSON 必须读全量才能 parse,建议限制 JSON 文件大小 - reader.readAsArrayBuffer(file); - } else { - // 图片/普通文本 读取前 2KB 足够判断头部和编码特征 - reader.readAsArrayBuffer(file.slice(0, 2048)); - } - }); - } + // 读取前 1KB 进行判断 + if (expectedMagic === 'TYPE_TEXT' && extension === 'json') { + // JSON 必须读全量才能 parse,建议限制 JSON 文件大小 + reader.readAsArrayBuffer(file); + } else { + // 图片/普通文本 读取前 2KB 足够判断头部和编码特征 + reader.readAsArrayBuffer(file.slice(0, 2048)); + } + }); + } } // 【demo】 @@ -269,4 +298,4 @@ export class FileValidator { // .catch((err) => { // statusDiv.textContent = `检测失败: ${err}`; // statusDiv.style.color = 'red'; -// }); \ No newline at end of file +// });