赞
踩
前端使用vue
<span id="response_row" class="result-streaming">{{ item.assistantContent }}</span>
.result-streaming:after {
-webkit-animation: blink 1s steps(5, start) infinite;
animation: blink 1s steps(5, start) infinite;
content: "▋";
margin-left: 0.25rem;
vertical-align: baseline;
}
先贴最后成功使用的
使用fetchEventSource方法
参考代码:https://blog.csdn.net/cuiyuchen111/article/details/129468291
参考/下载文档:https://www.npmjs.com/package/@microsoft/fetch-event-source?activeTab=readme
以下为后端接口要求
前端代码
<p v-if="item.requestFlag" class="content robot_content"><span id="response_row" class="result-streaming">{{ item.assistantContent }}</span></p>
<p class="content robot_content"><span v-html="item.assistantContent"></span></p>
async getResponseFromAPI() { const that = this; this.sendLoading = true; // 用户提问时间 let userTime = that.getNowTime(); const form = JSON.parse(JSON.stringify(this.form)); console.log(form, "请求里的form"); //物理添加 页面 that.conversations.push({ userContentId: "", userContent: form.prompt, userContentDatetime: userTime, assistantContentId: "", assistantContent: "", assistantContentDatetime: userTime, requestFlag: true, }); // 对话请求 const abortController = new AbortController(); let formData = new FormData(); formData.append("chatid", this.currentChatId); formData.append("clientid", form.clientid); formData.append("prompt", form.prompt); const url = "xxxxx"; const headers = new Headers(); const body = formData; const eventSource = fetchEventSource(url, { method: "POST", headers, body, signal: abortController.signal, onmessage(e) { that.handleScrollBottom(); that.form.prompt = ""; const response_row = document.getElementById("response_row"); console.log(e.data); let res = JSON.parse(e.data); let index = that.conversations.length - 1; if (res.message == "[DONE]") { res.data = JSON.parse(res.data); console.log(res.data); let obj = { userContentId: res.data.userContentId, userContent: res.data.userContent, userContentDatetime: userTime, assistantContentId: res.data.assistantContentId, assistantContent: res.data.assistantContent, assistantContentDatetime: that.getNowTime(), requestFlag: false, }; console.log(obj); that.$set(that.conversations, index, obj); that.sendLoading = false; abortController.abort(); eventSource.close(); console.log("我是结束!!"); } else { var content = res.data; response_row.innerText += content; // console.log(content) // if (content.includes("[ENTRY]")) { // content = content.replaceAll("[ENTRY]", "\n"); // } } }, onclose() { console.log("close"); that.sendLoading = false; abortController.abort(); eventSource.close(); }, onerror(error) { let index = that.conversations.length - 1; that.conversations.splice(index, 1); that.sendLoading = false; console.log("error", error); abortController.abort(); eventSource.close(); }, }); }
遇到的问题:
1.只调用一次事件 但fetch请求发送了两次或多次且终止失败
//按照fetchEventSource文档内的写法 请求暂停无效 const ctrl = new AbortController(); fetchEventSource('/api/sse', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ foo: 'bar' }), signal: ctrl.signal, }); //而后看到一种说法,fetchEventSource是针对EventSource API的,而不是xhr或fetch API //因此定义EventSource存储接口所返回的数据 使用EventSource的暂停方法 =》 fetchEventSource暂停成功 const eventSource = fetchEventSource(url, { method: "POST", headers, body, signal: abortController.signal, onmessage(e) { eventSource.close(); }, onclose() { eventSource.close(); }, onerror(error) { eventSource.close(); }, });
1.使用fetch方式进行sse流式处理
优点:可以使用post请求
缺点:获取到的数据处理困难 获取事件返回格式或有错误
参考代码:https://blog.csdn.net/betterAndBetter_/article/details/129900233
http://681314.com/A/YaHyYpjoPF
async function send() { const input = document.getElementById("input").value; const output = document.getElementById("output"); output.innerText = ""; const url = "/api/stream"; const data = { "Prompt": input }; //直接获取 Fetch 的response, 无法使用 await的话, Promise的方式也是可以的。 const response = await fetch(url, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" } }) //获取UTF8的解码 const encode = new TextDecoder("utf-8"); //获取body的reader const reader = response.body.getReader(); // 循环读取reponse中的内容 while (true) { const { done, value } = await reader.read(); if (done) { break; } // 解码内容 const text = encode.decode(value); // 当获取错误token时,输出错误信息 if (text === "<ERR>") { output.innerText = "Error"; break; } else { // 获取正常信息时,逐字追加输出 output.innerText += text; } } }
【记得补截图】
2.使用eventSource进行sse流式处理
优点:获取到的数据格式规范 易处理
缺点:无法使用post请求
参考b站视频:https://www.bilibili.com/video/BV1QA411C7mN/?spm_id_from=333.880.my_history.page.click&vd_source=384646ea9baa6985ceb5331bff5b87b0
var rsource = (this.rsource = new EventSource( `/api/chat/repeat/${this.cid}` )); rsource.addEventListener("open", function () { console.log("connect"); }); //如果服务器响应报文中没有指明事件,默认触发message事件 rsource.addEventListener("message", function (e) { console.log(`resp:(${e.data})`); var rconv = that.conversation[that.conversation.length - 1]; if (e.data == "[DONE]") { rsource.close(); rconv["loading"] = false; that.convLoading = false; that.refrechConversation(); that.rsource = undefined; return; } var content = e.data; if (content.includes("[ENTRY]")) { content = content.replaceAll("[ENTRY]", "\n"); } // 滚动到最下面 that.handleScrollBottom(); var idx = rconv.idx; rconv["speeches"][idx] += content; that.refrechConversation(); }); //发生错误,则会触发error事件 rsource.addEventListener("error", function (e) { console.log("error:" + e.data); rsource.close(); that.rsource = undefined; });
由于eventSource获取到的数据比fetch流畅许多,所以研究过eventSource能否使用post请求,使用过以下代码,但失败了
3.fetch和eventSource同时使用
优点:可以很顺利的请求并且获取到数据
缺点:fetch支持post eventSource不支持post 对接口请求方式有要求 几乎不太能兼容
// 获取表单元素 const form = document.querySelector('#my-form'); // 监听表单提交事件 form.addEventListener('submit', (event) => { event.preventDefault(); // 阻止默认提交行为 const formData = new FormData(form); // 创建 FormData 对象 // 发送 POST 请求并接收 SSE 流式输出 fetch('/api/submit-form', { method: 'POST', body: formData }).then((response) => { // 如果请求成功,则创建 EventSource 对象监听 SSE 输出 if (response.ok) { const eventSource = new EventSource('/api/stream'); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); console.log(data); // 处理接收到的数据 }; eventSource.onerror = (error) => { // 监听错误事件 console.error(error); }; } }).catch((error) => { console.error(error); }); });
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。