Vue.js无库实现图片上传至MongoDB报错求助
问题分析与修复方案
错误原因拆解
1. 选择图片后立即报错
给type="file"的输入框绑定v-model="skillData.image"会触发报错,因为File对象是只读的,Vue的双向绑定机制会尝试修改这个只读对象,导致浏览器抛出"Failed to set the 'value' property on 'HTMLInputElement'"类错误。
2. 提交表单后报错
- 验证阶段(
validateData)时,skillData.image是File对象,如果skillSchema的验证规则是针对字符串(base64)设计的,会直接验证失败。 - 提交前直接覆盖
skillData.value.image为base64字符串,可能导致后续依赖File对象的逻辑(比如二次验证)出错。 file类型输入框的@input事件触发时机不合理,会频繁触发resetError逻辑,干扰正常流程。
具体修复步骤
1. 移除file输入框的v-model绑定
file类型输入不支持Vue的双向绑定,直接通过@change事件处理文件选择:
<form-input-molecule label="Image" id="image" name="image" type="file" :error-message="validationErrors.skillCreationForm?.image" @change="previewImage" ></form-input-molecule>
2. 分离验证与提交的数据处理
验证时使用原始File对象,提交前再转换为base64,避免覆盖原始数据:
const createSkill = async () => { try { validationErrors.value["skillCreationForm"] = {}; // 用原始File对象做验证 const isValid = await validateData( ["skillCreationForm"], skillSchema, skillData.value ); if (!isValid) return; // 单独转换base64,不修改原skillData const imageBase64 = await toBase64(skillData.value.image); const submitData = { ...skillData.value, image: imageBase64, progress: parseInt(skillData.value.progress) }; await skillStore.createSkill(submitData); router.push({ name: "skills" }); } catch (error) { console.error(error); } };
3. 适配skillSchema的File对象验证规则
如果使用Yup做验证,需要针对File对象编写格式校验:
// skillSchema示例 import * as yup from 'yup'; export const skillSchema = yup.object().shape({ name: yup.string().required('名称必填'), image: yup.mixed() .required('图片必填') .test('file-type', '仅支持JPG/PNG/GIF格式', (value) => { return value && ['image/jpeg', 'image/png', 'image/gif'].includes(value.type); }), progress: yup.number().min(0).max(100).required('进度必填') });
4. 清理预览URL避免内存泄漏
组件卸载时释放createObjectURL创建的临时URL:
import { onUnmounted } from 'vue'; let currentPreviewUrl = null; const previewImage = (event) => { const preview = document.getElementById("preview-image"); skillData.value.image = event.target.files[0]; if (skillData.value.image) { // 先释放旧的预览URL if (currentPreviewUrl) URL.revokeObjectURL(currentPreviewUrl); currentPreviewUrl = URL.createObjectURL(skillData.value.image); preview.src = currentPreviewUrl; } else { preview.src = ""; } }; // 组件销毁时清理资源 onUnmounted(() => { if (currentPreviewUrl) URL.revokeObjectURL(currentPreviewUrl); });
内容的提问来源于stack exchange,提问作者g4rf4z
相关产品推荐
相关产品推荐

