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

@microsoft fetchEventSource 无法流式返回多语言翻译响应问题

问题描述

后端实现多语言翻译的流式响应,Postman测试时单个翻译完成后会立即返回结果,但在React项目中使用@microsoft/fetchEventSource发送POST请求时,需等所有翻译完成才会收到响应。

错误分析

从提供的代码中可定位出以下核心问题:

  • FormData缺失核心参数:SubmitHandler接收了input(待翻译文本)参数,但未将其添加到FormData中,后端无法获取待翻译内容,直接导致请求逻辑异常。
  • useEffect依赖项错误:当前useEffect依赖requestData?.signal,但requestData是包含FormData和signal的对象,每次调用SubmitHandler都会生成新的requestData对象,依赖signal无法触发useEffect重新执行,导致请求未正确发起或复用旧上下文。
  • 缺少SSE必要请求头:未声明Accept: text/event-stream,部分浏览器或代理会将请求当作普通HTTP请求处理,缓存整个响应直到完成后才返回。
  • fetchEventSource配置不全:POST请求的SSE需要开启keepalive: true维持连接,否则可能被浏览器主动断开,导致流式响应失效。
解决方案

修改useMultiTranslate Hook代码如下(注释标注关键修改点):

import { useRef, useState, useEffect } from "react";
import { useStore } from "@/store";
import { fetchEventSource } from '@microsoft/fetch-event-source';

export const useMultiTranslate = () => {
  const abortController = useRef(null);
  const src_lang = useStore((state) => state.srcLanguage);
  const [requestData, setRequestData] = useState(null);
  // 修正:正确解构store的状态更新函数(原解构方式会导致状态更新异常)
  const setTranslations = useStore((state) => state.setTranslations);

  const url = 'api/v2/infere/multi';
  
  useEffect(() => {
    if (!requestData) return;
    
    const { request, signal } = requestData;
    // 构建包含SSE必要字段的请求头
    const headers = {};
    if (sessionStorage.getItem("token")) {
      headers["Authorization"] = `Bearer ${sessionStorage.getItem("token")}`;
    }
    // 明确声明接受SSE流式响应
    headers["Accept"] = "text/event-stream";
    // 禁止缓存响应
    headers["Cache-Control"] = "no-cache";

    const streamTranslations = async () => {
      try {
        await fetchEventSource(url, {
          method: "POST",
          body: request,
          headers: headers,
          signal: signal,
          // 开启keepalive维持POST请求的SSE连接
          keepalive: true,
          // 允许页面隐藏时仍保持连接(可选,根据需求调整)
          openWhenHidden: true,
          onmessage(ev) {
            console.log(`Received event: ${ev.data}`);
            // 增加JSON解析容错
            try {
              const newTranslation = JSON.parse(ev.data);
              setTranslations((prev) => ({ ...prev, ...newTranslation }));
            } catch (parseErr) {
              console.error("Failed to parse event data:", parseErr);
            }
          },
          onerror(err) {
            console.error("Streaming error: ", err);
            // 抛出错误终止请求,避免无限重连
            throw err;
          },
          onclose() {
            console.log("Streaming connection closed");
          }
        });
      } catch (error) {
        // 排除主动中止请求的错误
        if (!signal.aborted) {
          console.error('Failed to stream response:', error);
        }
      }
    };

    streamTranslations();

    // 清理函数:终止旧请求,避免内存泄漏
    return () => {
      abortController.current?.abort();
    };
  }, [requestData]); // 修正:依赖整个requestData对象,确保每次请求变化都触发

  const SubmitHandler = (input, sId) => {
    // 终止之前的请求(如果存在)
    abortController.current?.abort();
    abortController.current = new AbortController();
    
    const request = new FormData();
    // 修正:添加待翻译文本到FormData
    request.append("text", input);
    request.append("src_lang", src_lang);
    request.append("mt_dest_lang", "multi");
    // 按需添加sId参数
    if (sId) request.append("sId", sId);

    const signal = abortController.current.signal;
    setRequestData({ request, signal });
  };

  return SubmitHandler;
};

同时修正TextboxToolbar中的调用逻辑:

const multiTranslate = () => {
  if (inputText.trim().length) {
    SubmitHandler(inputText.trim(), sId);
  }
};
额外检查点
  • 确认后端返回的SSE格式符合标准:每条消息以data: 开头,结尾用\n\n分隔,示例:
    data: {"lang": "zh", "translation": "你好"}
    
    data: {"lang": "en", "translation": "Hello"}
    
  • 检查前端是否有代理或拦截器(如axios拦截器)修改了请求头或响应处理,导致SSE被转换为普通响应。
  • 查看浏览器控制台是否存在CORS或其他网络错误,这类错误可能会中断流式响应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:23:08