[前端开发Vue]在Axios中,怎么实现实时监听上传数据进度、监听接口返回数据进度、如何终止网络请求。实现上传文件实时进度条以及下载文件实时进度条。

'# [前端开发Vue]在Axios中,怎么实现实时监听上传数据进度、监听接口返回数据进度、如何终止网络请求。实现上传文件实时进度条以及下载文件实时进度条

一、背景与问题

在现代Web开发中,文件上传/下载的进度可视化是提升用户体验的关键要素。传统Axios的axios.get/axios.post方法无法直接获取请求的实时进度信息,这在处理大文件传输时尤为明显。开发者常遇到以下问题:

  1. 无法实时监控文件上传/下载的进度百分比
  2. 无法在请求中途主动终止正在进行的网络请求
  3. 无法在前端实现精确的进度条渲染
  4. 无法处理请求中断后的资源清理

这些问题在文件上传(如用户头像、文档上传)和数据下载(如大体积API响应)场景中尤为突出。本文将深入探讨Axios的底层实现机制,结合Vue组件架构,提供完整的解决方案。

二、基本原理

Axios的请求流程包含三个关键阶段:

  1. 请求准备阶段:创建HTTP请求对象,配置请求头、超时等参数
  2. 传输阶段:网络请求的发送与数据传输
  3. 响应阶段:接收服务器响应并处理数据

Axios通过onUploadProgress和onDownloadProgress两个回调函数,分别监控请求体的上传进度和响应体的下载进度。这两个回调函数基于浏览器的XMLHttpRequest对象实现,其本质是通过监听progress事件来获取进度信息。

三、环境准备

确保项目中已安装Axios和Vue的依赖:

npm install axios vue

在Vue组件中需要使用Axios的拦截器来统一处理进度监控,同时需要处理跨域问题(如果存在)。

四、核心实现

1. 上传文件进度条实现

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <div v-if="progress > 0">
      上传进度:{{ progress }}%
      <progress :value="progress" max="100"></progress>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      file: null,
      progress: 0
    };
  },
  methods: {
    handleFileChange(event) {
      this.file = event.target.files[0];
    },
    async uploadFile() {
      const formData = new FormData();
      formData.append('file', this.file);
      
      const config = {
        onUploadProgress: (progressEvent) => {
          // 计算上传百分比
          const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          this.progress = percent;
        }
      };
      
      try {
        const response = await axios.post('/upload', formData, config);
        console.log('上传成功:', response.data);
      } catch (error) {
        console.error('上传失败:', error);
      }
    }
  }
};
</script>

关键代码解析:

  • FormData对象用于处理文件上传
  • onUploadProgress回调接收progressEvent对象,包含loaded(已传输字节数)和total(总字节数)
  • 进度计算使用Math.round()确保百分比整数化
  • progress数据绑定到模板中的进度条组件

2. 下载文件进度条实现

<template>
  <div>
    <button @click="downloadFile">下载文件</button>
    <div v-if="progress > 0">
      下载进度:{{ progress }}%
      <progress :value="progress" max="100"></progress>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      progress: 0
    };
  },
  methods: {
    async downloadFile() {
      const config = {
        onDownloadProgress: (progressEvent) => {
          const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          this.progress = percent;
        }
      };
      
      try {
        const response = await axios.get('/download', config);
        console.log('下载成功:', response.data);
      } catch (error) {
        console.error('下载失败:', error);
      }
    }
  }
};
</script>

关键代码解析:

  • onDownloadProgress回调用于监控响应体的下载进度
  • 同样使用loaded/total计算百分比
  • 注意下载文件时需要处理Content-Length头信息,若服务器未设置该头,需通过response.headers['content-length']获取

3. 请求终止机制实现

<template>
  <div>
    <button @click="uploadFile">上传文件</button>
    <button @click="cancelUpload">取消上传</button>
    <div v-if="progress > 0">
      上传进度:{{ progress }}%
      <progress :value="progress" max="100"></progress>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      file: null,
      progress: 0,
      cancelToken: null
    };
  },
  methods: {
    handleFileChange(event) {
      this.file = event.target.files[0];
    },
    async uploadFile() {
      const formData = new FormData();
      formData.append('file', this.file);
      
      // 创建取消令牌
      this.cancelToken = axios.CancelToken.source();
      
      const config = {
        onUploadProgress: (progressEvent) => {
          const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          this.progress = percent;
        },
        cancelToken: this.cancelToken.token
      };
      
      try {
        const response = await axios.post('/upload', formData, config);
        console.log('上传成功:', response.data);
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('上传已取消');
        } else {
          console.error('上传失败:', error);
        }
      }
    },
    cancelUpload() {
      if (this.cancelToken) {
        this.cancelToken.cancel('用户主动取消上传');
      }
    }
  }
};
</script>

关键代码解析:

  • 使用axios.CancelToken创建取消令牌
  • 在请求配置中注入cancelToken对象
  • 通过cancelToken.cancel()方法主动终止请求
  • 使用axios.isCancel()判断是否为取消请求

五、完整案例

文件上传下载进度条完整案例

<template>
  <div>
    <h2>文件上传</h2>
    <input type="file" @change="handleFileChange" />
    <button @click="uploadFile">上传文件</button>
    <button @click="cancelUpload">取消上传</button>
    <div v-if="uploadProgress > 0">
      上传进度:{{ uploadProgress }}%
      <progress :value="uploadProgress" max="100"></progress>
    </div>
    
    <h2>文件下载</h2>
    <button @click="downloadFile">下载文件</button>
    <div v-if="downloadProgress > 0">
      下载进度:{{ downloadProgress }}%
      <progress :value="downloadProgress" max="100"></progress>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      uploadFile: null,
      uploadProgress: 0,
      downloadProgress: 0,
      uploadCancelToken: null,
      downloadCancelToken: null
    };
  },
  methods: {
    handleFileChange(event) {
      this.uploadFile = event.target.files[0];
    },
    async uploadFile() {
      const formData = new FormData();
      formData.append('file', this.uploadFile);
      
      this.uploadCancelToken = axios.CancelToken.source();
      
      const config = {
        onUploadProgress: (progressEvent) => {
          const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          this.uploadProgress = percent;
        },
        cancelToken: this.uploadCancelToken.token
      };
      
      try {
        const response = await axios.post('/upload', formData, config);
        console.log('上传成功:', response.data);
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('上传已取消');
        } else {
          console.error('上传失败:', error);
        }
      }
    },
    cancelUpload() {
      if (this.uploadCancelToken) {
        this.uploadCancelToken.cancel('用户主动取消上传');
      }
    },
    async downloadFile() {
      const config = {
        onDownloadProgress: (progressEvent) => {
          const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          this.downloadProgress = percent;
        },
        cancelToken: this.downloadCancelToken ? this.downloadCancelToken.token : axios.CancelToken.source().token
      };
      
      try {
        const response = await axios.get('/download', config);
        console.log('下载成功:', response.data);
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('下载已取消');
        } else {
          console.error('下载失败:', error);
        }
      }
    },
    cancelDownload() {
      if (this.downloadCancelToken) {
        this.downloadCancelToken.cancel('用户主动取消下载');
      }
    }
  }
};
</script>

关键代码解析:

  • 同时处理上传和下载的进度条
  • 为上传和下载分别创建独立的取消令牌
  • 在下载时处理可能的Content-Length缺失情况
  • 通过axios.isCancel()判断请求是否被取消

六、源码解析

Axios的onUploadProgress和onDownloadProgress的实现原理如下:

// axios/lib/core/createAxios.js
function createAxios(config) {
  const httpAdapter = config.httpAdapter || getAdapter(config);
  
  return function axios(config) {
    const instanceConfig = mergeConfig(config, this.defaults);
    const { headers = {}, timeout, ...rest } = instanceConfig;
    
    const request = (url, config) => {
      const { headers, timeout, ...rest } = mergeConfig(config, instanceConfig);
      
      const xhr = new XMLHttpRequest();
      
      // 上传进度监听
      xhr.upload.onprogress = (event) => {
        if (event.lengthComputable) {
          config.onUploadProgress({
            loaded: event.loaded,
            total: event.total
          });
        }
      };
      
      // 下载进度监听
      xhr.onprogress = (event) => {
        if (event.lengthComputable) {
          config.onDownloadProgress({
            loaded: event.loaded,
            total: event.total
          });
        }
      };
      
      // ...其他配置
    };
  };
}

关键点说明:

  1. 使用XMLHttpRequest的upload对象监听上传进度
  2. 使用XMLHttpRequest的onprogress事件监听下载进度
  3. lengthComputable属性用于判断是否可以获取总大小
  4. 进度计算基于event.loaded和event.total的比值

七、进阶使用

1. 使用AbortController替代CancelToken

const controller = new AbortController();
axios.post('/upload', data, {
  signal: controller.signal
});

// 中止请求
controller.abort();

优势:

  • 更符合现代浏览器的API规范
  • 支持AbortSignal的onabort事件
  • 与Fetch API兼容性更好

2. 结合Vue的响应式系统

import { ref, onMounted } from 'vue';

const progress = ref(0);
const isUploading = ref(false);

onMounted(() => {
  const controller = new AbortController();
  
  axios.post('/upload', data, {
    signal: controller.signal,
    onUploadProgress: (event) => {
      progress.value = Math.round((event.loaded / event.total) * 100);
    }
  }).finally(() => {
    isUploading.value = false;
  });
});

关键点:

  • 使用Vue的ref管理状态
  • 在onUploadProgress中更新响应式数据
  • 使用finally处理请求完成后的状态更新

3. 带缓存的进度监控

const cache = new Map();

function getCache(key) {
  if (cache.has(key)) {
    return cache.get(key);
  }
  return null;
}

function setCache(key, value) {
  cache.set(key, value);
}

应用场景:

  • 多个并发请求的进度合并展示
  • 避免重复计算相同的进度信息
  • 提升性能的缓存策略

八、性能与工程实践

1. 性能优化策略

优化措施说明
节流处理使用setInterval代替频繁的进度更新
二进制处理使用ArrayBuffer进行大数据处理
压缩传输使用Gzip压缩减少传输体积
工具库使用使用axios-progress-bar等第三方库

示例代码:

let progressInterval = null;

onUploadProgress(event) {
  if (!progressInterval) {
    progressInterval = setInterval(() => {
      this.progress = Math.round((event.loaded / event.total) * 100);
    }, 100);
  }
}

2. 异常处理机制

try {
  await axios.post('/upload', data, {
    onUploadProgress: (event) => {
      this.progress = Math.round((event.loaded / event.total) * 100);
    }
  });
} catch (error) {
  if (axios.isCancel(error)) {
    console.log('请求已取消');
  } else {
    console.error('请求异常:', error.message);
  }
}

关键点:

  • 区分取消请求和网络异常
  • 处理AbortError和NetworkError等不同类型的错误
  • 清理可能残留的定时器或资源

3. 安全性考虑

  1. CSRF防护:在上传文件时需要校验CSRF Token
  2. 文件类型验证:在前端进行文件类型校验,防止恶意文件上传
  3. 大小限制:设置合理的文件大小限制,防止资源耗尽
  4. 敏感数据加密:对上传的敏感数据进行加密处理

九、常见问题与踩坑

1. 进度计算不准确

问题现象:

  • 进度条显示0%或不更新
  • 进度百分比计算错误

常见原因:

  • 未设置Content-Length头
  • 使用了multipart/form-data格式
  • 服务器未正确发送Content-Length头

解决方案:

  • 使用axios.get时通过response.headers['content-length']获取大小
  • 对于multipart/form-data,通过event.total获取总大小
  • 在服务器端设置正确的Content-Length头

2. 请求无法取消

问题现象:

  • 调用cancel()方法无效
  • 请求仍然在后台运行

常见原因:

  • 未正确创建和注入CancelToken
  • 使用了fetch而非axios进行请求
  • 使用了不支持取消的HTTP客户端

解决方案:

  • 确保使用axios.CancelToken创建取消令牌
  • 在请求配置中正确注入cancelToken
  • 避免混用不同的HTTP客户端

3. 跨域问题

问题现象:

  • 浏览器报错:XMLHttpRequest cannot load ... No 'Access-Control-Allow-Origin' header is present on the requested resource

解决方案:

  • 配置CORS策略
  • 使用axios的withCredentials选项
  • 在后端设置Access-Control-Allow-Origin头
  • 使用代理服务器处理跨域请求

十、最佳实践

场景推荐方案
文件上传使用onUploadProgress配合FormData
文件下载使用onDownloadProgress配合Content-Length
请求取消使用CancelToken或AbortController
大文件处理使用分块传输和进度条展示
响应式更新使用Vue的响应式系统更新状态
安全校验在前端进行文件类型和大小校验
性能优化使用节流处理和缓存策略

推荐实践:

  1. 对所有文件上传/下载请求都添加进度监控
  2. 为每个请求创建独立的取消令牌
  3. 使用AbortController代替CancelToken(现代浏览器)
  4. 对敏感数据进行加密处理
  5. 设置合理的文件大小限制

十一、总结

在Vue项目中使用Axios实现实时进度监控和请求终止,需要深入理解Axios的底层机制,特别是XMLHttpRequest的onprogress事件和CancelToken机制。通过合理使用onUploadProgress和onDownloadProgress回调,可以实现精确的上传/下载进度条,并通过取消令牌实现请求的主动终止。

在实际开发中,需要注意:

  • 不同的HTTP方法和Content-Type对进度计算的影响
  • 跨域请求的特殊处理
  • 大文件处理时的性能优化
  • 安全性校验和数据加密
  • 响应式系统的状态更新

建议根据具体业务场景选择合适的实现方式,对于大文件传输和重要业务请求,推荐使用AbortController结合Fetch API实现更现代的解决方案。通过合理的设计和实践,可以显著提升用户体验和系统稳定性。

VUE , ios
最后修改于:2026年09月29日 06:42

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日