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

如何实现REST API先确认文件接收再后台处理Excel并返回数据?

可行解决方案

因为HTTP协议是单次请求对应单次响应,所以你没法在返回200之后再给同一个请求返回处理结果。针对你的场景,推荐以下几种实用方案:

方案1:任务ID轮询模式

这是最通用、兼容性最好的方案,流程如下:

  1. 客户端上传文件后,API立即返回200,同时返回一个唯一的taskId
  2. 客户端拿着taskId定期调用查询接口,获取处理状态和结果
  3. 服务端后台异步处理Excel,将状态和结果存在缓存(比如Redis)或数据库中

代码示例

上传接口

const { v4: uuidv4 } = require('uuid'); // 需先安装:npm install uuid
const redis = require('redis'); // 需先安装:npm install redis
const client = redis.createClient();

app.post('/upload', upload.fields([{name: 'raw'}, {name: 'report'}]), async (req, res) => {
  // 生成唯一任务ID
  const taskId = uuidv4();

  // 立即返回接收成功和任务ID
  res.status(200).json({
    code: 200,
    message: '文件已接收,处理中',
    taskId
  });

  // 后台异步执行Excel处理,不阻塞响应
  (async () => {
    try {
      const result = await excelOperations(req.files);
      // 将处理结果存入缓存,设置1小时过期时间
      await client.setEx(`task:${taskId}`, 3600, JSON.stringify({
        status: 'success',
        data: result
      }));
    } catch (err) {
      await client.setEx(`task:${taskId}`, 3600, JSON.stringify({
        status: 'failed',
        message: err.message
      }));
    }
  })();
});

查询结果接口

app.get('/task-result', async (req, res) => {
  const { taskId } = req.query;
  if (!taskId) {
    return res.status(400).json({ message: '缺少taskId' });
  }

  const taskData = await client.get(`task:${taskId}`);
  if (!taskData) {
    return res.status(404).json({ message: '任务不存在或已过期' });
  }

  res.json(JSON.parse(taskData));
});

方案2:Server-Sent Events(SSE) 主动推送

如果客户端需要实时获取结果,不需要轮询,可以用SSE,它允许服务端主动向客户端推送消息,流程:

  1. 客户端上传文件后,建立SSE连接
  2. API返回文件接收成功的消息,后台异步处理Excel
  3. 处理完成后,通过SSE连接推送结果给客户端

代码示例

上传+SSE接口

app.post('/upload-sse', upload.fields([{name: 'raw'}, {name: 'report'}]), (req, res) => {
  // 设置SSE响应头
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  // 先发送文件接收成功的消息
  res.write(`data: ${JSON.stringify({ message: '文件已接收,处理中' })}\n\n`);

  // 后台异步处理Excel
  (async () => {
    try {
      const result = await excelOperations(req.files);
      // 推送处理结果
      res.write(`data: ${JSON.stringify({ status: 'success', data: result })}\n\n`);
    } catch (err) {
      res.write(`data: ${JSON.stringify({ status: 'failed', message: err.message })}\n\n`);
    } finally {
      // 关闭连接
      res.end();
    }
  })();
});

客户端示例(浏览器端)

const formData = new FormData();
formData.append('raw', rawFile);
formData.append('report', reportFile);

fetch('/upload-sse', {
  method: 'POST',
  body: formData
}).then(res => {
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  
  reader.read().then(function processResult({ done, value }) {
    if (done) return;
    const data = decoder.decode(value);
    const message = JSON.parse(data.replace('data: ', ''));
    console.log(message);
    // 这里可以更新页面UI,比如显示处理状态
    if (message.status) {
      // 处理完成,做后续操作
    }
    return reader.read().then(processResult);
  });
});

方案3:回调URL模式

如果客户端有自己的服务,可以让客户端上传时提供一个回调地址,服务端处理完成后主动调用这个地址推送结果:

  1. 客户端上传文件时,在请求参数或header中携带callbackUrl
  2. API立即返回文件接收成功的响应
  3. 后台处理完成后,调用callbackUrl发送结果

代码示例

app.post('/upload-callback', upload.fields([{name: 'raw'}, {name: 'report'}]), async (req, res) => {
  const { callbackUrl } = req.body;
  if (!callbackUrl) {
    return res.status(400).json({ message: '缺少callbackUrl' });
  }

  // 立即返回接收成功
  res.status(200).json({ message: '文件已接收,处理中' });

  // 后台异步处理Excel
  (async () => {
    try {
      const result = await excelOperations(req.files);
      // 调用回调URL推送结果
      await fetch(callbackUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ status: 'success', data: result })
      });
    } catch (err) {
      await fetch(callbackUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ status: 'failed', message: err.message })
      });
    }
  })();
});

选择建议

  • 如果客户端是普通网页应用,优先用任务ID轮询模式,实现简单,兼容性好
  • 如果需要实时性且客户端是浏览器,用SSE,比WebSocket轻量
  • 如果客户端是服务端应用,用回调URL模式,耦合度低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:02:08