一. 需求与问题:

      后端返回响应标头 content-type:application/x-ndjson 格式接口如下,在服务器中执行回车可以正常输出流式结果,但是在postman和vue3项目中总是等结果全部请求完成后才会整体返回,前端需做适配请求处理。

curl http://192.168.3.37:12345/api/generate -d '{
  "model": "Q2KL",
  "prompt":"Why is the sky blue?",
  "stream": true
}'

二:解决办法

 1.  在public文件夹下新建config.js文件,用于定义外部接口,并在index.html文件中引入该文件,发布测试环境前可将该路径替换为线上地址后打包。

 2.  使用fetch()函数发送一个请求,并对请求返回的 response 进行处理的逻辑,代码如下:
const generateData = ref("");
const generateStr = ref("");
// 在pubilc文件夹中添加config.js文件定义外部接口,并在index.html文件中引入该config.js文件
const generatePath = window?.SYSCONFIG?.generateUrl;
// 定义一个名为useChatFileApi的箭头函数,用于处理后端返回流式输出实现前端吐字功能
const useChatFileApi = () => {
  console.log(window.SYSCONFIG);
  generateStr.value = queryForm.value.content;
  const requestData = {
    model: "Q2KL",
    prompt: queryForm.value.content,
    stream: true,
  };

  fetch(generatePath, {
    method: "POST",
    headers: {
      "Content-Type": "application/json", // 发送 JSON 数据时的 Content-Type
    },
    body: JSON.stringify(requestData),
  })
    .then((response: any) => {
      // 检查响应的状态码是否表示请求成功(通常状态码在200 - 299之间表示成功)
      if (!response.ok) {
        throw new Error("Network response was not ok");
      }
      // 检查响应是否有实际的主体内容
      if (!response.body) {
        return;
      }
      // 创建一个用于读取响应流中数据块的读取器对象reader
      // response.body?.getReader()是获取可读取流的读取器,这里的any类型声明可能是因为缺少更精确的类型定义
      const reader: any = response.body?.getReader();
      // 创建一个TextDecoder对象decoder,用于将读取到的二进制数据解码为文本数据
      const decoder = new TextDecoder();

      // 定义一个函数processTextStream,用于处理每次读取到的数据流数据块
      const processTextStream = ({ done, value }) => {
        // 当done为true时,表示数据流已经读取完毕
        if (done) {
          console.log("Stream complete");
          return;
        }
        // 当done为false时,表示还有数据需要处理
        // 将读取到的二进制数据块value使用decoder解码为文本数据chunk
        const chunk = decoder.decode(value, { stream: true });
        // 将文本数据chunk按照换行符进行分割,得到一个包含每行文本的数组lines
        const lines = chunk.split("\n");
        // 遍历数组lines中的每一行line
        for (const line of lines) {
          // 如果该行文本不为空(通过line.trim()判断)
          if (line.trim()) {
            try {
              // 尝试将该行文本解析为JSON对象
              const data = JSON.parse(line);
              // console.log(data); // 处理每个数据对象
              // 将 JSON 对象转换为字符串并累加到 accumulatedData 中
              // 这里你可以根据需要格式化数据
              generateData.value += data.response;
            } catch (parseError) {
              console.error("Error parsing JSON:", parseError);
            }
          }
        }

        // 继续读取下一个数据块
        return reader.read().then(processTextStream);
      };

      // 开始读取数据流,每次读取到一个数据块后就会调用processTextStream函数进行处理
      // 并且通过链式调用的方式不断读取下一个数据块,直到数据流读取完毕
      return reader.read().then(processTextStream);
    })
    // 捕获在整个请求处理过程中(包括发送请求、处理响应等环节)出现的任何错误
    // 并将错误信息输出到控制台
    .catch((error) => console.error("Error:", error));
};
</script>
3.  发布测试环境需配置nginx,解决vue项目跨域,并将项目访问端口通过 /api/ 映射到指定请求地址
server {
    listen  8889;
    server_name  localhost; # 修改为docker服务宿主机的ip
        #   指定允许跨域的方法,*代表所有
        add_header Access-Control-Allow-Methods *;

        #   预检命令的缓存,如果不缓存每次会发送两次请求
        add_header Access-Control-Max-Age 3600;
        #   带cookie请求需要加上这个字段,并设置为true
        add_header Access-Control-Allow-Credentials true;

        #   表示允许这个域跨域调用(客户端发送请求的域名和端口) 
        #   $http_origin动态获取请求客户端请求的域   不用*的原因是带cookie的请求不支持*号
        add_header Access-Control-Allow-Origin $http_origin;

        #   表示请求头的字段 动态获取
        add_header Access-Control-Allow-Headers 
        $http_access_control_request_headers;

        #   OPTIONS预检命令,预检命令通过时才发送请求
        #   检查请求的类型是不是预检命令
        if ($request_method = OPTIONS){
            return 200;
        }
    location / {
        root   /usr/share/nginx/html;
	    index  index.html index.htm;
        try_files $uri $uri/ /index.html =404;
    }
    location /analysis/ {
        proxy_pass http://192.168.3.37:8081/analysis/;
        proxy_set_header Host $host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    location /api/ {
        proxy_pass http://192.168.3.37:12345/api;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header User-Agent $http_user_agent; 
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐