核心原理是利用 XMLHttpRequest 的 upload.onprogress 事件监听上传进度,Fetch 不支持原生上传进度;封装 useUpload Hook 返回响应式状态和方法,基于 xhr 实现进度更新、中断、错误处理与 Promise 封装。

用 Composition API 监听文件上传进度的核心原理
浏览器原生 XMLHttpRequest 和 Fetch API 中,只有 XMLHttpRequest 支持上传过程的进度事件(upload.onprogress)。Fetch 本身不暴露上传进度,但可通过 ReadableStream + TransformStream 手动分块读取并上报进度——不过实际项目中更推荐封装基于 XMLHttpRequest 的可靠方案。
封装一个可复用的 useUpload Hook
目标:返回 upload 方法、progress(0–100 数值)、isUploading、error、response,支持中断、重试、自定义 headers。
- 使用
ref管理响应式状态(progress、isUploading等) - 内部创建
XMLHttpRequest实例,绑定upload.onprogress更新进度 - 将
abort暴露为函数,便于手动取消 - 支持 Promise 风格调用,也支持 await / then 链式处理
示例代码(useUpload.js):
import { ref } from 'vue'
<p>export function useUpload() {
const progress = ref(0)
const isUploading = ref(false)
const error = ref(null)
const response = ref(null)
let xhr = null</p><p>const upload = (url, file, options = {}) => {
return new Promise((resolve, reject) => {
isUploading.value = true
error.value = null
progress.value = 0
response.value = null</p><pre class="brush:php;toolbar:false;"> xhr = new XMLHttpRequest()
// 上传进度
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
progress.value = Math.round((e.loaded / e.total) * 100)
}
}
// 完成
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status {
error.value = new Error('Network error')
reject(error.value)
}
// 超时
xhr.ontimeout = () => {
error.value = new Error('Request timeout')
reject(error.value)
}
xhr.open('POST', url, true)
xhr.timeout = options.timeout ?? 30000
// 设置 headers(如 token)
Object.entries(options.headers || {}).forEach(([k, v]) => {
xhr.setRequestHeader(k, v)
})
// 构造 FormData
const formData = new FormData()
formData.append(options.fieldName || 'file', file)
xhr.send(formData)
})}
使用 `@ainative/vue-sdk` 为 Vue 3 应用添加 AI 聊天功能。适用于以下场景:(1) 安装 `@ainative/vue-sdk`,(2) 在 Vue 组件中使用 `useChat` 组合式函数,(3) 提供……
const abort = () => { if (xhr && xhr.readyState !== XMLHttpRequest.DONE) { xhr.abort() isUploading.value = false progress.value = 0 } }
return { upload, abort, progress, isUploading, error, response } }
在组件中使用 useUpload
直接解构调用,无需关心底层细节:
<script setup>
import { useUpload } from './useUpload'
<p>const { upload, abort, progress, isUploading, error, response } = useUpload()<p>const handleFileChange = async (e) => {
const file = e.target.files?.[0]
if (!file) return<p>try {
await upload('/api/upload', file, {
headers: { Authorization: 'Bearer xxx' },
fieldName: 'avatar'
})
console.log('上传成功:', response.value)
} catch (err) {
console.error('上传失败:', err)
}
}
</script><p><template><input type="file"><div v-if="isUploading">上传中:{{ progress }}%</div>
<button v-if="isUploading">取消上传</button>
<div v-if="error">错误:{{ error.message }}</div>
</template></p>
进阶建议:支持多文件、断点续传与类型泛化
若需扩展能力,可在基础 hook 上叠加:
- 添加
multiple: boolean参数,内部遍历上传并合并进度(加权平均或按完成数) - 结合后端分片接口,用
File.slice()分块上传,维护 chunk index 与校验逻辑 - 用泛型约束
response类型(配合 TypeScript),例如useUpload<uploadresult>()</uploadresult> - 将上传配置(超时、重试次数、最大并发)抽为默认参数或全局配置项
不复杂但容易忽略:记得在组件卸载前调用 abort() 防止内存泄漏或状态错乱(可用 onBeforeUnmount 自动清理)。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










