PHP后端
php端主要是用到ob_flush和flush,头改为流式。
基本代码
代码如下:
<?php
header('Content-Type:text/event-stream');
header('Cache-Control:no-cache');
header('Connection:keep-alive');
function streamPostRequest($url,$data){
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST,true);
curl_setopt($ch,CURLOPT_POSTFIELDS,json_encode($data));
curl_setopt($ch,CURLOPT_HTTPHEADER,[
'Content-Type:application/json',
'Accept:text/event-stream'
]);
curl_setopt($ch,CURLOPT_WRITEFUNCTION,function($ch,$data){
//如果数据不需要特殊处理,这里可以直接返回
echo $data;
ob_flush();
flush();
//这里返回数据的长度,如果有特殊处理需要返回处理后的数据长度
return strlen($data);
});
curl_exec($ch);
curl_close($ch);
}
以上代码数据没有特殊处理,数据都会被前端接受到。如果特殊处理前端可能需要在done为true时候接收最后一条数据。
数据特殊处理
如果对数据进行处理,在CURLOPT_WRITEFUNCTION中写,代码如下:
<?php
curl_setopt($ch,CURLOPT_WRITEFUNCTION,function($ch,$data){
$start=strpos($data,'{');
$end=strrpos($data,'}');
$dataContentStr=substr($data,$start,$end-$start);
$dataContent=json_decode($dataContentStr,true);
$r='';
if($dataContent['docs']){
//如果这里逻辑复杂,会导致数据返回延迟,需要在前端done为true时接收
/*code....*/
$r=$data.'test';
}else{
$r=$data;
}
echo $r;
ob_flush();
flush();
//这里为最终数据的长度
return strlen($r);
});
方法的使用
注意这里要使用file_get_contents接受数据
<?php
//要使用file_get_contents接受数据
$data=file_get_contents('php://input')
$dataArray=json.decode($data,true);
$content=$dataArray['content'];
$json=[
'query'=>$content,
];
streamPostRequest("http://127.0.0.1:80/test",$json);
前端
基本代码
用的fetch请求方法,这个可以传数据
var abortController:any=null
var generteSwich=false //中断请求用的
var maxTime=0
async function fetchStreamWithToken(url:string,data:any){
abortController=new AbortController()
var i=0
const response=await fetch(url,{
method:'POST',
headers:{
'Content-Type':'application/json',
'Accept':'text/event-stream',
'token':getToken() as any,
},
body:JSON.stringify($data),
signal:abortController.signal
})
//获取可读流
const reader=response.body?.getReader()
const decoder=new TextDecoder('utf-8')
let buffer:any=''
generateSwich=true
while(true){
const {done,value}:any=await reader?.read()
if(done || !generateSwich){
//如果php数据没直接返回,数据进行特殊处理后返回,需要接受最后数据
let endData=buffer
//结束
setTimeout(()=>{
//对最后一个数据的处理,处理逻辑按照需求写
const end=JSON.parse()
if(end.test){
//code...
}
//更新会话方法
updateDialogue()
generateSwich=false //停止
dialogueList.value[diaId][lth-1].state='success'
},maxTime)
break
}
buffer+=decoder.decode(value,{steam:true})
//按行展开
const lines=buffer.split('\n')
buffer=lines.pop()
for(const line of lines){
if(line){
try{
const start=line.indexOf('{')
const end=line.lastIndexOf('}')
if(start===-1 || end===-1){
continue;
}
const content=line.substring(start,end+1)
const parsed=JSON.parse(content)
let t=parsed.text
setTimeout(()=>{
if(!generateSwich){
r eturn
}
dialogueList.value[diaId][lth-1].content+=t
if(!dialogueList.value[diaId][lth-1].message_id){
dialogueList.value[diaId][lth-1].message_id=parsed.message_id
}
},60*i)
i++
maxTime=60*i
}catch(error){
console.log(error)
console.log('原始数据',line)
}
}
}
}
}
结束思考方法
//停止思考
function stopThinking(){
abortController.abort()
generateSwich=false
}