You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Nodejs的Express向前端分块发送响应实现进度实时推送

实现思路

你这个场景用HTTP分块流式响应即可满足单API请求内持续推送进度的需求,不需要额外建立长连接,逻辑轻量且完全适配你现有的技术栈:

后端(Express)实现步骤

  • 首先配置响应头,开启分块传输、禁用缓存:
const express = require('express');
const app = express();
const progress = require('progress-stream');

app.post('/api/process-file', async (req, res) => {
  // 核心响应头配置
  res.setHeader('Content-Type', 'text/plain; charset=utf-8');
  res.setHeader('Transfer-Encoding', 'chunked');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  // 跨域场景下添加该配置,允许前端读取流式内容
  res.setHeader('Access-Control-Expose-Headers', '*');
  • 分步执行业务逻辑,每步进度更新时往响应流推送JSON格式的进度事件,每次推送后调用res.flush()确保内容实时发出:
try {
    // 步骤1:上传S3,结合progress-stream获取上传进度
    const fileStream = fs.createReadStream(req.file.path);
    const progressStream = progress({ 
      length: req.file.size, 
      time: 100 // 每100ms返回一次进度
    });
  
    progressStream.on('progress', (p) => {
      // 推送步骤1实时进度
      res.write(JSON.stringify({
        step: 1,
        status: 'processing',
        percent: Math.round(p.percentage),
        msg: '文件上传中'
      }) + '\n'); // 加换行符避免多个事件粘包
      res.flush?.();
    });

    // 将progressStream传入你已有的S3上传方法即可
    await uploadToS3(progressStream, req.file.originalname);
    // 步骤1完成推送
    res.write(JSON.stringify({ step: 1, status: 'completed', percent: 100, msg: '文件上传完成' }) + '\n');
    res.flush?.();

    // 步骤2:文件编码处理
    res.write(JSON.stringify({ step: 2, status: 'processing', percent: 0, msg: '文件编码中' }) + '\n');
    res.flush?.();
    await processFileEncoding(req.file.originalname);
    res.write(JSON.stringify({ step: 2, status: 'completed', percent: 100, msg: '文件编码完成' }) + '\n');
    res.flush?.();

    // 步骤3:更新数据库
    res.write(JSON.stringify({ step: 3, status: 'processing', percent: 0, msg: '数据更新中' }) + '\n');
    res.flush?.();
    await updateDatabaseRecord(req.file.originalname);
    // 全流程完成推送
    res.write(JSON.stringify({ step: 3, status: 'completed', percent: 100, msg: '全部任务完成' }) + '\n');
    res.flush?.();

    // 所有步骤完成后结束响应
    res.end();
  } catch (err) {
    // 异常处理,推送错误后结束响应
    res.write(JSON.stringify({ status: 'error', msg: err.message }) + '\n');
    res.end();
  }
})

前端(Vue+Axios)实现步骤

  • 配置Axios开启流式响应,监听分块内容实时更新UI:
// 发起请求方法
async function uploadAndProcess(file) {
  const formData = new FormData();
  formData.append('file', file);

  await axios.post('/api/process-file', formData, {
    // 开启流式响应
    responseType: 'stream',
    // 监听下载进度事件,接收服务端推送的分块内容
    onDownloadProgress: (progressEvent) => {
      // 解码收到的内容,按换行拆分事件
      const chunks = progressEvent.event.target.responseText.split('\n');
      // 取最新的有效事件
      const latestEvent = chunks.filter(Boolean).pop();
      if (latestEvent) {
        const progressData = JSON.parse(latestEvent);
        // 更新UI逻辑
        updateProgressUI(progressData);
      }
    }
  });
}

// UI更新逻辑,完全匹配你的交互需求
function updateProgressUI(data) {
  if (data.status === 'error') {
    alert(`处理失败:${data.msg}`);
    return;
  }
  switch(data.step) {
    case 1:
      // 步骤1更新进度条
      this.uploadPercent = data.percent;
      break;
    case 2:
      // 步骤2展示自定义加载提示
      this.loadingTip = data.msg;
      break;
    case 3:
      if (data.status === 'completed') {
        // 步骤3完成弹出成功弹窗
        this.$modal.success('任务处理完成');
      }
      break;
  }
}

注意事项

  • 如果你用了Express的compression压缩中间件,必须在每次res.write后调用res.flush(),否则内容会被缓存无法实时推送到前端
  • 流式响应的每个事件用换行符分隔,避免前端收到多个事件粘包无法解析
  • 步骤执行过程中如果出现异常,一定要推送错误事件后调用res.end()结束响应,避免前端请求一直挂起

内容的提问来源于stack exchange,提问作者Anirudh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 18:09:04