SurveyJS+TS文件上传字段如何实现上传前的自定义类型校验?
核心原因说明
SurveyJS 的文件题校验触发逻辑和值绑定机制决定了现有配置必然存在延迟:
- 自定义表达式校验器依赖的
{document}字段值,只有在文件上传成功、回调执行完成并把文件元数据写入字段后才会存在 - 原生文件上传流程顺序为:用户选中文件 → 触发
onFileUpload回调 → 调用上传API → 成功后写入字段值 → 触发表单校验 - 这种流程下,所有自定义校验必然在API请求发出后才执行,不合法的文件会直接打到接口层。
落地实现方案
1. 在onFileUpload回调中前置执行类型校验
在回调内拿到用户选中的文件列表后,在调用上传API之前先完成扩展名校验,校验不通过直接中断流程,把错误提示绑定到当前文件题组件上,SurveyJS会自动在输入区域下方展示错误。
修改后的onFileUpload实现:
// 定义业务允许的文件扩展名集合 const ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg', 'pdf']; async onFileUpload(surveyInstance, options) { if (!options.files?.length) return; // 前置校验:筛选不满足类型要求的文件 const hasInvalidFile = options.files.some((file: File) => { const fileExtension = file.name.split('.').pop()?.toLowerCase() || ''; return !ALLOWED_EXTENSIONS.includes(fileExtension); }); // 存在不合法文件时终止上传 if (hasInvalidFile) { // 将错误绑定到当前文件题,组件会自动展示提示 options.question.error = 'Invalid file format, please upload your document in png, jpeg or pdf format.'; // 不调用options.callback,SurveyJS会自动清理选中的无效文件 return; } // 校验通过后走原有上传逻辑 const targetFile = options.files[0]; try { const uploadRes: any = await this.authService.uploadFile(targetFile.name, targetFile, targetFile.type); options.callback( 'success', options.files.map((item: File) => { return { file: item, content: uploadRes.code }; }) ); // 原有a标签替换逻辑保留 const aTags = document.querySelectorAll(`[href='${uploadRes.code}']`); if (aTags.length) { (aTags[0] as HTMLAnchorElement).href = uploadRes.documentUrl; (aTags[0] as HTMLAnchorElement).innerHTML = uploadRes.name; (aTags[0] as HTMLAnchorElement).target = '_blank'; } } catch (e) { // 保留原有API异常的处理逻辑 options.question.error = 'File upload failed, please retry later.'; } }
2. 清理冗余校验逻辑
完成前置校验后,原JSON配置中依赖自定义函数的expression校验器已经失去实际作用,可以直接从validators数组中移除,避免重复校验带来的性能损耗。
另外需要注意:JSON中配置的acceptedTypes属性仅控制浏览器原生文件选择框的格式过滤,用户可以通过拖拽、粘贴文件的方式绕过这个限制,不能完全依赖该属性做合法性校验。
3. 多文件题场景的统一拦截方案
如果表单中存在多个文件题,且每个题的允许上传类型不同,可以直接在SurveyJS全局事件中统一拦截,不需要在每个题的onFileUpload回调中重复写校验逻辑:
// 初始化Survey实例时绑定全局上传拦截事件 survey.onUploadFile.add((survey, options) => { // 直接读取题目配置中已定义的acceptedTypes值作为校验规则 const allowedTypes = options.question.acceptedTypes ?.split(',') .map(type => type.trim().replace('.', '').toLowerCase()) || []; // 校验文件类型 const hasInvalidFile = options.files.some(file => { const fileExt = file.name.split('.').pop()?.toLowerCase() || ''; return !allowedTypes.includes(fileExt); }); if (hasInvalidFile) { options.question.error = `Unsupported file type, allowed formats: ${options.question.acceptedTypes}`; // 返回false直接终止上传流程 return false; } });
这种方式下,后续调整不同题目允许的文件类型,只需要修改JSON配置中的acceptedTypes字段值即可,不需要改动任何TypeScript代码。
内容的提问来源于stack exchange,提问作者Somnath Singh
相关产品推荐
相关产品推荐

