组件:
<a-upload
style="display: inline-block;width:auto;"
:file-list="item.fileList"
@success="uploadSuccess($event)"
@change="uploadChange($event,item.fileList,item)"
@error="uploadError($event)"
:custom-request="customUpload"
>
<template #upload-button>
<a-button type="outline"><icon-upload class="margin-right-10" />上传文件</a-button>
</template>
</a-upload>
函数:
import { getToken } from '@/utils/auth';
import {
ref,
} from 'vue';
import md5 from 'md5';
import axios from 'axios';
import { Message } from '@arco-design/web-vue';
export const customUpload=async(options:any)=>{
//-begin--不用全局的,用局部的获取headers----
const token = getToken();
const timestamp = ref(Date.parse(new Date().toString()) / 1000);
const passstr = ref(md5(import.meta.env.VITE_ENCRYPT + timestamp.value));
const headers_msg = ref({
apiverify: window.btoa(passstr.value + '#' + timestamp.value),
Authorization: token,
});
const action = window?.globalConfig.Root_url_dev + '/upload';
// -end----------
// console.log("options---->",options);
/**
* options:由 a-upload 传入的对象,包含以下关键字段:
fileItem: 当前上传的文件对象(含 file 属性)
onProgress(percent, file): 通知上传进度的回调
onSuccess(response, file): 上传成功时调用
onError(error): 上传失败时调用
*/
const {fileItem,onProgress,onSuccess,onError} = options;
const formData=new FormData(); //将用户选择的文件附加到 FormData 中,用于 multipart/form-data 请求。
formData.append('file',fileItem.file);
/**
* 使用 setTimeout 手动触发取消(防止 axios 内置 timeout 在某些环境下失效);
同时配合 axios.CancelToken 实现请求取消。
*/
//设置超时时间 (单位:毫秒)
const TIMEOUT=1000*60*30; //30分钟
//方案1:使用axios 的CancelToken
const source=axios.CancelToken.source(); //
//设置超时定时器
const timeoutTimer=setTimeout(()=>{
source.cancel(`上传超时(${TIMEOUT/1000}秒)`);
},TIMEOUT);
try{
//发送请求
console.log('headers.value', headers_msg.value);
/**
* action:上传接口地址(来自组件外部定义);
headers.value:包含认证 token 和时间戳签名(防重放攻击);
onUploadProgress:实时更新上传进度条。
*/
const response=await axios.post(action,formData,{
headers:{
...headers_msg.value,
'Content-Type':'multipart/form-data'
},
cancelToken:source.token,
timeout:TIMEOUT, // axios 内置超时
/* onUploadProgress:(progressEvent)=>{
console.log("上传进度--》",progressEvent);
if(progressEvent.lengthComputable){
const percent=Math.round(
(progressEvent.loaded * 100) / progressEvent.total
)
onProgress(percent,fileItem.file);
}
} */
});
/**
* 清除超时定时器;
调用 onSuccess,传递后端返回的数据(response.data),符合 a-upload 预期格式。
*/
//调用进度回调
//清除超时定时器
clearTimeout(timeoutTimer);
console.log('response', response);
//调用成功回调
onSuccess({
response:response.data, //保持与原来相同的结构
file:fileItem.file
})
}catch(error:Error|any){
console.log("error--->",error);
//清除超时定时器
if(timeoutTimer) clearTimeout(timeoutTimer);
let errorMessage = '上传失败';
if(axios.isCancel(error)){
errorMessage = error.message; // 上传超过 30秒
}else if(error.code === 'ECONNABORTED'){
errorMessage='请求超时,请检查网络连接';
}else if(error.response){
errorMessage=error.response.data?.message || `服务器错误:${error.response.status}`;
}else if(error.request){
errorMessage='请求失败,请检查网络连接';
}
//调用错误回调
onError(new Error(errorMessage));
}
}
export const uploadSuccess=(res: any) =>{
// console.log('itemvalue--->', itemvalue);
// console.log('item--->', item);
console.log('上传成功', res);
const resp_msg=res?.response?.response;
if(resp_msg?.code == 0){
Message.success('上传成功');
}else{
Message.error(resp_msg?.message);
}
}
export const uploadError=(res: any)=> {
console.log('上传失败', res);
Message.error(res?.response?.message);
}
/**
* const formData = new FormData();
formData.append('file', fileItem.file);
定义:FormData 是浏览器原生提供的一个构造函数,用于构建表单数据,特别适用于通过 XMLHttpRequest 或 fetch/axios 发送 multipart/form-data 格式的请求(常用于文件上传)。
用途:
可以动态添加键值对(包括文件、字符串等);
自动设置正确的 Content-Type(含 boundary);
被 axios.post() 等方法识别并正确编码。
*/
/**
* const source = axios.CancelToken.source();
定义:这是 Axios 提供的一种请求取消机制(虽然现在已 deprecated,但仍在广泛使用)。
返回值:source 是一个对象,包含两个属性:
token:一个取消令牌(CancelToken),传给 axios 请求;
cancel(message?):一个函数,调用它即可取消使用该 token 的请求。
⚠️ 注意:Axios 官方推荐未来改用 AbortController,但当前代码仍有效。
*/
/**
* await axios.post(action, formData, {
cancelToken: source.token,
// ...
});
作用:将 source.token 传递给 axios 请求,绑定取消能力。
效果:
如果后续调用了 source.cancel('reason'),这个请求会立即被中断,并抛出一个特殊的错误(可通过 axios.isCancel(error) 判断);
常用于:超时控制、用户手动取消、组件卸载时清理请求等场景。
*/
//
// 🔜 建议(面向未来)
// 虽然当前代码能正常工作,但因 CancelToken 已废弃,长期建议迁移到 AbortController:
/* const controller = new AbortController();
// 设置超时
const timeoutTimer = setTimeout(() => {
controller.abort();
}, TIMEOUT);
try {
const response = await axios.post(action, formData, {
headers: { ... },
signal: controller.signal, // 替代 cancelToken
timeout: TIMEOUT,
});
} catch (error) {
if (axios.isCancel(error) || error.name === 'AbortError') {
// 处理取消
}
} */
//