vue3+setup 处理后端返回流式输出接口,实现吐字功能
后端提供如下接口,在服务器中执行回车可以正常输出流式结果,但是在postman和vue3项目中请求总是等结果全部请求完成后才会整体返回
·
一. 需求与问题:
后端返回响应标头 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;
}
}
更多推荐


所有评论(0)