ai流式文字返回前端和php的处理办法
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中写,代码如下:
<?phpcurl_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=0async function fetchStreamWithToken(url:string,data:any){abortController=new AbortController()var i=0const 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=truewhile(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.textsetTimeout(()=>{if(!generateSwich){r eturn}dialogueList.value[diaId][lth-1].content+=tif(!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
}