PHP强行刷新浏览器缓存实现实时显示
响应头“X-Accel-Buffering”传递“yes”或“no”可以动态地开启或关闭代理的缓冲功能
设置此连接的代理缓存,将此设置为 no 将允许适用于 Comet 和 HTTP 流式应用程序的无缓冲响应
//后端代码
date_default_timezone_set('PRC'); //设置中国时区 header('X-Accel-Buffering: no'); header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); ob_end_clean(); ob_implicit_flush(1); //echo "retry: 10000\n"; while(1){ $data = [ "id" => time(), "message" => '欢迎来到helloweba,现在是北京时间'.date('Y-m-d H:i:s') ]; returnEventData($data); sleep(2); } function returnEventData($returnData, $event='message', $id=0, $retry=0){ //$ResponseType = ['message','event']; $str = ''; if($id>0){ $str .= "id: {$id}".PHP_EOL; } if($event){ $str.= "event: {$event}".PHP_EOL; } if($retry>0){ $str .= "retry: {$retry}".PHP_EOL; } if(is_array($returnData)){ $returnData = json_encode($returnData); } $str .= "data: {$returnData}".PHP_EOL; $str .= PHP_EOL; echo $str; }
发起请求
//定义函数 function fetchStream(url, params) { /* eslint-disable */ const {onmessage, onclose, ...otherParams} = params; var decoder = new TextDecoder(); const onProgress = function (textChunk, callback) { var WAITING = -1; var CLOSED = 2; var AFTER_CR = -1; var FIELD_START = 0; var FIELD = 1; var VALUE_START = 2; var VALUE = 3; var currentState = WAITING; var textBuffer = ""; var state = FIELD_START; var dataBuffer = ""; var lastEventIdBuffer = ""; var eventTypeBuffer = ""; var lastEventId = ""; var fieldStart = 0; var valueStart = 0; let c; var n = -1; for (var i = 0; i < textChunk.length; i += 1) { c = textChunk.charCodeAt(i); if (c === "\n".charCodeAt(0) || c === "\r".charCodeAt(0)) { n = i; } } const chunk = (n !== -1 ? textBuffer : "") + textChunk.slice(0, n + 1); for (let position = 0; position < chunk.length; position += 1) { c = chunk.charCodeAt(position); if (state === AFTER_CR && c === "\n".charCodeAt(0)) { state = FIELD_START; } else { if (state === AFTER_CR) { state = FIELD_START; } if (c === "\r".charCodeAt(0) || c === "\n".charCodeAt(0)) { if (state !== FIELD_START) { if (state === FIELD) { valueStart = position + 1; } var field = chunk.slice(fieldStart, valueStart - 1); var value = chunk.slice(valueStart + (valueStart < position && chunk.charCodeAt(valueStart) === " ".charCodeAt(0) ? 1 : 0), position); if (field === "data") { dataBuffer += "\n"; dataBuffer += value; } else if (field === "id") { lastEventIdBuffer = value; } else if (field === "event") { eventTypeBuffer = value.toLowerCase(); } } if (state === FIELD_START) { if (dataBuffer !== "") { lastEventId = lastEventIdBuffer; if (eventTypeBuffer === "") { eventTypeBuffer = "message"; } var event = new MessageEvent(eventTypeBuffer, { data: dataBuffer.slice(1), lastEventId: lastEventIdBuffer }); callback(event) /* if (eventTypeBuffer === "open") { callback(event) } else if (eventTypeBuffer === "message") { callback(event) } else if (eventTypeBuffer === "error") { callback(event) }*/ if (currentState === CLOSED) { return; } } dataBuffer = ""; eventTypeBuffer = ""; } state = c === "\r".charCodeAt(0) ? AFTER_CR : FIELD_START; } else { if (state === FIELD_START) { fieldStart = position; state = FIELD; } if (state === FIELD) { if (c === ":".charCodeAt(0)) { valueStart = position + 1; state = VALUE_START; } } else if (state === VALUE_START) { state = VALUE; } } } } }; const push = (controller, reader, contentType) => { reader.read().then((result)=>{ if (result.done) { controller.close(); onclose?.(); } else { controller.enqueue(result.value); var value = decoder.decode(result.value, {stream: true}) if (value){ if (/^text\/event\-stream(;.*)?$/i.test(contentType)) { try { JSON.parse(value) onmessage(new MessageEvent('error', { data: value, lastEventId: 0 })) }catch (e){ onProgress(value, onmessage) } } else { onmessage(new MessageEvent('error', { data: value, lastEventId: 0 })) } } push(controller, reader,contentType); } }).catch(()=>{ onclose?.(); }); }; // 发送请求 return fetch(url, otherParams) .then((response) => { // 以ReadableStream解析数据 const reader = response.body.getReader(); return new ReadableStream({ start(controller) { push(controller, reader, response.headers.get("Content-Type")); }, }); }).catch(()=>{ onclose?.(); }); } //调用函数示例 let theFirstTime = true;//是否首次回调 let allContent = '';//存储全部内容 fetchStream(url, { method: 'post', body: JSON.stringify({}), headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json', }, onmessage: (event) => { if (!event.data){ return } var data, text try { data = JSON.parse(event.data) switch (event.type) { case 'message': text = data.text break; case 'error': if (data.ret) { app.$message.error(data.msg); } break; } } catch (e) { text = event.data } if(text){ allContent += text //这里可以其他回调函数 //callback(text, theFirstTime, allContent) if (theFirstTime) { theFirstTime = false } } }, onclose: () => { //关闭时 } })
评论