重试按钮必须绑定原始File对象,存于choose回调的pendingFiles数组并配唯一id;须用unified:false模式配合FormData手动构造请求,且前后端需协同防重复(前端禁用按钮+后端校验md5或fileId)。

重试按钮必须绑定到原始 File 对象,不能依赖 obj.upload()
多文件上传失败后,obj.files 通常已被清空或索引错位,obj.upload() 调用会静默失败或传入 undefined。真正能重传的,只有你在 choose 回调里亲手存下的 File 实例。
操作要点:
- 在
choose中用Array.from(obj.files)转成数组,并为每个文件生成唯一id(如Date.now() + i),存入闭包变量(如pendingFiles = []) - 同时维护一个状态映射
statusMap = { [id]: 'failed' },在error回调中更新 - 预览 DOM 节点必须带
data-id属性,值与该文件的id一致,方便点击时精准查找
重试请求要用原生 fetch 或 XMLHttpRequest,禁用 unified: true
unified: true 模式下所有文件打包一次发送,失败即整批作废,根本不存在“单个重试”概念。必须设为 unified: false(默认值),才能对单个 File 构造独立请求。
示例重试逻辑(放在重试按钮 click 事件中):
const file = pendingFiles.find(f => f.id === fileId);
if (!file) return;
const formData = new FormData();
formData.append('file', file);
// 补充你原本的配置字段,如 token、category 等
formData.append('token', uploadCfg.headers.token);
fetch(uploadCfg.url, {
method: 'POST',
body: formData,
headers: { 'X-Requested-With': 'XMLHttpRequest' } // 避免被后端当成普通表单
})
.then(r => r.json())
.then(res => {
if (res.code === 0) {
statusMap[fileId] = 'success';
document.querySelector(`[data-id="${fileId}"]`).style.borderColor = '#52c418';
}
})
.finally(() => {
document.querySelector(`[data-id="${fileId}"] .retry-btn`).disabled = false;
});
防重复提交和后端幂等,两个锁缺一不可
用户手抖连点、网络延迟导致请求未返回就又点一次——这种场景下,同一文件可能被上传三遍。前端禁用按钮只是第一道防线,后端没做校验照样存三份。
必须同步做两件事:
- 前端:点击重试按钮后立即
btn.disabled = true,done或error后再恢复 - 后端:要求上传接口接收文件
md5或fileId(前端生成并随请求发出),入库前先查是否存在;不存在才写入,存在则直接返回已有 URL - 额外建议:在请求参数里加
ts或req_id,便于排查日志中哪次是重复请求
before 回调里不能改 data,动态参数得靠 FormData
很多人想在 error 里改 obj.config.data.retry = true,指望重传时带上这个字段——没用。data 是静态对象,只在初始化时读取一次,且不参与重试流程。
真正要带动态参数(比如重试次数、来源标记),必须在构造 FormData 时手动 .append():
formData.append('retry_count', (statusMap[fileId]?.retryCount || 0) + 1);
formData.append('source', 'retry_click');
这个细节容易被忽略:重试不是“再次走一遍 upload 流程”,而是你另起炉灶发请求。所有参数、头信息、超时控制,都得自己重新组织。











