From 223836d0ea6b3e9df3615ee0e15517c464dd8ad1 Mon Sep 17 00:00:00 2001 From: xuxiao6 <1797494661@qq.com> Date: Wed, 8 Jul 2026 10:57:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(vue-renderless/file-upload):=20[file-upload?= =?UTF-8?q?]=20accept=20=E6=94=AF=E6=8C=81=20video/*=E3=80=81audio/*=20?= =?UTF-8?q?=E9=80=9A=E9=85=8D=E7=AC=A6=20(fixes=20#4237)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isAcceptType 此前仅对 image/* 走扩展名白名单校验,video/*、audio/* 会落入 正则分支,其中的通配符 * 被当作正则量词,导致 mp4、mp3 等合法文件被误拦截。 改为统一的 MIME 通配符映射,复用 FILE_TYPE.VIDEO / FILE_TYPE.AUDIO 白名单。 补充 accept 通配符单元测试,用 BeforeUploadOptions 替代 as any。 Co-authored-by: Cursor --- packages/renderless/src/file-upload/index.ts | 13 ++- .../file-upload/__tests__/accept-type.test.ts | 87 +++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 packages/vue/src/file-upload/__tests__/accept-type.test.ts diff --git a/packages/renderless/src/file-upload/index.ts b/packages/renderless/src/file-upload/index.ts index a709ad42e4..defdbfc52f 100644 --- a/packages/renderless/src/file-upload/index.ts +++ b/packages/renderless/src/file-upload/index.ts @@ -206,9 +206,18 @@ const onBeforeIsPromise = ({ } const isAcceptType = (acceptArray, file, constants, fileType) => { + // MIME 通配符(image/*、video/*、audio/*)需按扩展名白名单校验, + // 否则 type 里的 * 会被当作正则量词,导致 video/*、audio/* 无法匹配到对应文件 + const mimeWildcardMap = { + [constants.IMAGE_TYPE]: constants.FILE_TYPE.PICTURE, + 'video/*': constants.FILE_TYPE.VIDEO, + 'audio/*': constants.FILE_TYPE.AUDIO + } + return acceptArray.some((type) => { - if (type.toLowerCase() === constants.IMAGE_TYPE) { - return constants.FILE_TYPE.PICTURE.split('/').includes(fileType) + const whitelist = mimeWildcardMap[type.trim().toLowerCase()] + if (whitelist) { + return whitelist.split('/').includes(fileType) } return new RegExp(`(${type.trim()})$`, 'i').test(file.name) }) diff --git a/packages/vue/src/file-upload/__tests__/accept-type.test.ts b/packages/vue/src/file-upload/__tests__/accept-type.test.ts new file mode 100644 index 0000000000..e960339057 --- /dev/null +++ b/packages/vue/src/file-upload/__tests__/accept-type.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, test, vi } from 'vitest' +import { beforeUpload } from '@opentiny/vue-renderless/file-upload' +import { $constants as constants } from '../src/index' + +/** + * accept 使用 MIME 通配符(image/*、video/*、audio/*)时的校验。 + * 修复 #4237:此前仅 image/* 走扩展名白名单,video/*、audio/* 会落入正则分支, + * 通配符 * 被当作正则量词,导致 mp4、mp3 等合法文件被误拦截。 + */ + +const t = (_key: string, params?: { format?: string }) => + params?.format ? `格式(.${params.format})暂不支持` : '暂不支持' + +type BeforeUploadOptions = Parameters[0] + +const createUploadFn = (accept: string, modalMessage = vi.fn()) => + beforeUpload({ + props: { accept }, + api: { handleRemove: vi.fn() }, + Modal: { message: modalMessage }, + constants, + t, + state: { isEdm: false, triggerClickType: '' } + // 单测仅构造 accept 校验所需的最小依赖,props/api/state 为部分 mock + } as BeforeUploadOptions) + +const makeFile = (name: string, type: string) => ({ + name, + raw: new File([], name, { type }) +}) + +describe('file-upload accept 通配符校验', () => { + test('accept=video/* 时 mp4 应通过校验并上传', () => { + const modalMessage = vi.fn() + const doUpload = vi.fn() + + createUploadFn('video/*', modalMessage)(makeFile('测试.mp4', 'video/mp4'), false, doUpload) + + expect(modalMessage).not.toHaveBeenCalled() + expect(doUpload).toHaveBeenCalled() + }) + + test('accept=video/* 时非视频文件仍应被拦截', () => { + const modalMessage = vi.fn() + const doUpload = vi.fn() + + createUploadFn('video/*', modalMessage)(makeFile('测试.txt', 'text/plain'), false, doUpload) + + expect(modalMessage).toHaveBeenCalledWith( + expect.objectContaining({ + message: '格式(.txt)暂不支持', + status: 'warning' + }) + ) + expect(doUpload).not.toHaveBeenCalled() + }) + + test('accept=audio/* 时 mp3 应通过校验并上传', () => { + const modalMessage = vi.fn() + const doUpload = vi.fn() + + createUploadFn('audio/*', modalMessage)(makeFile('测试.mp3', 'audio/mpeg'), false, doUpload) + + expect(modalMessage).not.toHaveBeenCalled() + expect(doUpload).toHaveBeenCalled() + }) + + test('对照:accept=image/* 时 jpg 可通过校验', () => { + const modalMessage = vi.fn() + const doUpload = vi.fn() + + createUploadFn('image/*', modalMessage)(makeFile('测试.jpg', 'image/jpeg'), false, doUpload) + + expect(modalMessage).not.toHaveBeenCalled() + expect(doUpload).toHaveBeenCalled() + }) + + test('对照:accept=.mp4 精确扩展名仍可通过校验', () => { + const modalMessage = vi.fn() + const doUpload = vi.fn() + + createUploadFn('.mp4', modalMessage)(makeFile('测试.mp4', 'video/mp4'), false, doUpload) + + expect(modalMessage).not.toHaveBeenCalled() + expect(doUpload).toHaveBeenCalled() + }) +})