Vue组件中如何缓存文件上传,避免ERR_UPLOAD_FILE_CHANGED错误?
解决Vue组件中「ERR_UPLOAD_FILE_CHANGED」的前端文件缓存方案
问题背景
开发的Vue组件支持CSV文件上传、预览处理结果,确认后执行系统变更,但浏览器偶尔抛出「ERR_UPLOAD_FILE_CHANGED」错误——这是因为用户在上传后到确认执行的间隙修改了原文件,浏览器检测到文件内容/元数据变化,阻止后续上传操作。
解决方案:前端缓存已上传文件
核心思路是在用户首次上传时,将文件内容完整读取并缓存到内存(或IndexedDB,针对大文件),后续确认执行时直接提交缓存的内容,而非依赖原文件对象。
方案1:内存缓存(适合小体积CSV文件)
利用FileReader读取文件内容,转为Blob后重新构造File对象缓存,彻底脱离对本地原文件的依赖。
<template> <div> <input type="file" accept=".csv" @change="handleFileUpload" /> <button @click="previewResult">预览结果</button> <button @click="confirmChange" :disabled="!cachedFile">确认执行变更</button> </div> </template> <script setup> import { ref } from 'vue' const cachedFile = ref(null) // 缓存处理后的File对象 const originalFileName = ref('') const handleFileUpload = (e) => { const file = e.target.files[0] if (!file) return originalFileName.value = file.name const reader = new FileReader() reader.onload = (event) => { // 将读取到的内容转为Blob,再构造新的File对象 const blob = new Blob([event.target.result], { type: file.type }) cachedFile.value = new File([blob], originalFileName.value, { type: file.type, lastModified: Date.now() // 固定修改时间,避免浏览器检测到原文件变化 }) } reader.readAsArrayBuffer(file) } const previewResult = () => { if (!cachedFile.value) return // 这里用cachedFile.value做预览处理,比如解析CSV内容展示 // ... 预览逻辑 } const confirmChange = async () => { if (!cachedFile.value) return // 构造FormData提交缓存的文件 const formData = new FormData() formData.append('csvFile', cachedFile.value) try { await fetch('/api/submit-change', { method: 'POST', body: formData }) // 提交成功后清空缓存 cachedFile.value = null originalFileName.value = '' } catch (err) { console.error('提交失败:', err) } } </script>
方案2:IndexedDB缓存(适合大体积CSV文件)
如果CSV文件体积较大(比如超过100MB),内存缓存可能导致性能问题,此时用IndexedDB持久化缓存文件内容,流程完成后再删除。
<template> <!-- 同方案1的模板结构 --> </template> <script setup> import { ref } from 'vue' import { openDB } from 'idb' // 先安装依赖:npm install idb const cachedFileKey = 'uploaded-csv-file' const originalFileName = ref('') // 初始化IndexedDB实例 const dbPromise = openDB('FileCacheDB', 1, { upgrade(db) { db.createObjectStore('files') } }) const handleFileUpload = async (e) => { const file = e.target.files[0] if (!file) return originalFileName.value = file.name const arrayBuffer = await file.arrayBuffer() // 将文件内容存入IndexedDB const db = await dbPromise await db.put('files', arrayBuffer, cachedFileKey) } const previewResult = async () => { const db = await dbPromise const arrayBuffer = await db.get('files', cachedFileKey) if (!arrayBuffer) return // 构造File对象用于预览解析 const file = new File([arrayBuffer], originalFileName.value, { type: 'text/csv' }) // ... 预览逻辑 } const confirmChange = async () => { const db = await dbPromise const arrayBuffer = await db.get('files', cachedFileKey) if (!arrayBuffer) return // 构造File对象提交 const file = new File([arrayBuffer], originalFileName.value, { type: 'text/csv' }) const formData = new FormData() formData.append('csvFile', file) try { await fetch('/api/submit-change', { method: 'POST', body: formData }) // 提交成功后删除缓存 await db.delete('files', cachedFileKey) originalFileName.value = '' } catch (err) { console.error('提交失败:', err) } } </script>
关键注意点
- 彻底脱离原File对象:原File对象关联本地文件,一旦文件被修改就会触发错误,必须用缓存内容重新构造独立的File对象。
- 及时清理缓存:流程完成(成功/失败)后务必清空缓存,避免占用内存或存储资源。
- 文件校验:上传时严格校验文件类型为
.csv,避免无效文件进入缓存流程。
内容的提问来源于stack exchange,提问作者Zack Stone
相关产品推荐
相关产品推荐

