uniapp在微信小程序中實現 SSE 流式響應

前言

最近需要使用uniapp開發一個智能對話頁面,其中就需要使用SSE進行通信。

本文介紹下在uniapp中如何基于uni.request實現SSE流式處理。

在線體驗

#小程序:yinuosnowball

SSE傳輸格式

返回輸出的流式塊:

  1. Content-Type為text/event-stream

  2. 每個流式塊均為 data: 開頭,塊之間以 \n\n 即兩個換行符分隔, 如下所示:
    在這里插入圖片描述

  3. 后端接口定義的數據如下: - event為message,開始接收數據,answer為返回的結果 - event為message_end結束

在這里插入圖片描述

接口數據已約定完成,下一步使用uniapp開始接收處理數據。

uniapp處理數據

客戶端實現在微信小程序中接收 SSE 流式響應,需要以下步驟:

  1. 配置 HTTP 請求:設置適當的請求頭和參數,以確保服務器返回流式響應。
  2. 處理分塊數據:由于SSE是分塊傳輸的,我們需要監聽每個數據塊,并解析它們。
  3. 錯誤處理:當每一次返回的最后出現不是完整的響應時,需要進行特殊處理。
  4. 完成時:可以進行追問等額外處理

下面使用 uni.request實現SSE的案例:

基本框架:

const requestTask = uni.request({url,method: 'POST',header: {Accept: 'text/event-stream',Authorization,},data,enableChunked: true,responseType: 'arraybuffer',success: (res) => {console.log('Data received 數據接受完畢:', res.data)},fail: (error) => {console.log('打印***error 錯誤處理', error)},complete: (complete) => {console.log('打印***complete 完成接收', complete)}
})requestTask.onChunkReceived((res)=>{// 處理數據
})

通過對requestTaskonChunkReceived監聽就可以得到數據塊,通過打印我們可以看到數據返回是ArrayBuffer,我們需要進行處理。

在這里插入圖片描述

 const uint8Array = new Uint8Array(res.data);let text = String.fromCharCode.apply(null, uint8Array);

解析后得到以data:data:返回的格式,

此處需要注意解析后是data: 還是 data:data:格式

在這里插入圖片描述

進一步處理:由于返回的數據塊不是一段一段,而是很多段都返回,因此我們需要進行\n\n進行拆分,然后逐個解析:

在這里插入圖片描述

const arr = text.split('\n\n').filter(Boolean)
arr.forEach(msg => {const jsonStr = msg.substring(11); // 去掉 'data:data: ' 前綴const data = JSON.parse(jsonStr);switch (data.event) {case 'message': {// 拼接返回文本this.dialogueList[existingMessageIndex].answer += data.answer;break;}case 'message_end':// 消息結束break;}
});

至此,我們就可以接收到消息,如果就這樣那就最好,但對接的過程發現,每一次返回的文本最后一段不是完整的,導致解析出現失敗,如下圖所示:

在這里插入圖片描述

解決方案是: 定義一個變量,當解析出現失敗時,肯定是最后一段,進行存儲,下一次接收到數據將上一次存儲的進行拼接,然后解析:

在這里插入圖片描述

具體代碼:

// 每次發送存儲數據
const msgObj = {query,answer: "",conversationId: null,isDone: false
}
this.dialogueList.push(msgObj)
// 保存上一次失敗的text
let lastText = ''
requestTask.onChunkReceived((res) => {// 第一步:獲取 字符串 數組const uint8Array = new Uint8Array(res.data);let text = lastText + String.fromCharCode.apply(null, uint8Array);lastText = '';let arr = text.split('\n\n').filter(Boolean)let lastIndex = arr.length - 1// 第二步:是否可以直接進行解析try {// 判斷是否可以全部解析完成arr.every(item => JSON.parse(item.substring(11)))} catch (error) {// 如果報錯截取最后一項lastText = arr[lastIndex]arr = arr.filter((_, i) => i !== lastIndex)}// 處理數據塊if (arr.length) {try {arr.forEach(msg => {const jsonStr = msg.substring(11); // 去掉 'data: ' 前綴const data = JSON.parse(jsonStr);const existingMessageIndex = this.dialogueList.findIndex(item => item === msgObj);switch (data.event) {case 'message': {// 查找是否存在相同ID的消息this.dialogueList[existingMessageIndex].answer += data.answer;break;}case 'message_end':// 消息結束break;}});} catch (error) {console.error('解析數據失敗:', error);}}
});

這樣就完成了對不連續返回的錯誤處理。

如果需要直接結束請求,可以直接使用requestTask.abort()

完整代碼

function sendMsg(query) {const msgObj = {query, // 問題answer: "", // 回答的結果conversationId: null,feedback: null,isDone: false // 自定義格式,用于加載處理}this.dialogueList.push(msgObj)// 請求參數const data = {}this.requestTask = uni.request({url,method: 'POST',header: {Accept: 'text/event-stream',Authorization: getStorage(tokenKeyEnum.zhonglv),},data,enableChunked: true,responseType: 'arraybuffer',success: (res) => {console.log('Data received 數據接受完畢:', res.data)},fail: (error) => {console.log('打印***error 錯誤處理', error)},complete: (complete) => { console.log('打印***complete 完成接收', complete)}})let lastText = ''this.requestTask.onChunkReceived((res) => {// 第一步:獲取 字符串 數組const uint8Array = new Uint8Array(res.data);let text = lastText + String.fromCharCode.apply(null, uint8Array);lastText = '';let arr = text.split('\n\n').filter(Boolean)let lastIndex = arr.length - 1// 第二步:是否可以直接進行解析try {let isCanResolve = arr.every(item => JSON.parse(item.substring(11)))console.log('打印***isCanResolve', isCanResolve)} catch (error) {// 如果報錯截取最后一項lastText = arr[lastIndex]arr = arr.filter((_, i) => i !== lastIndex)console.log('打印***error', error)}// 處理數據塊if (arr.length) {try {arr.forEach(msg => {const jsonStr = msg.substring(11); // 去掉 'data:data: ' 前綴const data = JSON.parse(jsonStr);const existingMessageIndex = this.dialogueList.findIndex(item => item === msgObj);switch (data.event) {case 'message': {// 查找是否存在相同ID的消息this.dialogueList[existingMessageIndex].answer += data.answer;break;}case 'message_end':// 消息結束this.dialogueList[existingMessageIndex].isDone = true;break;}});} catch (error) {console.error('解析數據失敗:', error);}}});
}

總結

最后總結一下,在uniapp中使用uni.request處理流式響應,主要步驟有:

  • 開啟:enableChunked: true
  • 設置請求Header:Accept: 'text/event-stream'
  • 注冊數據接收響應函數: requestTask.onChunkReceived(onChunkReceived)
  • 分塊數據解析 String.fromCharCode
  • 處理不連續返回問題
  • 結束:requestTask.abort()

希望對你有所幫助,如有錯誤,請指正 O^O!


參考文檔

  • uni.request文檔: uniapp.dcloud.net.cn/api/request…

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/922460.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/922460.shtml
英文地址,請注明出處:http://en.pswp.cn/news/922460.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STM32N6AI資料匯總

文章目錄前言一、STM32N6硬件資源1.1 NUCLEO-N657X0-Q1.2 STM32N6570-DK1.3 正點原子STM32N647二、STM32N6軟件資源2.1 STM32CubeN6例程資源包2.2 STM32圖像信號處理器(ISP)調優軟件2.3 正點原子N6開發板配套軟件三、AI軟件資源3.1 STM32N6 AI軟件包總結…

Flask學習筆記(一)

1、環境準備pip install Flask使用Flask開發第1個入門程序:from flask import Flask app Flask(__name__) app.route(/) def hello_world():return Hello, World!if __name__ __main__:app.run()Flask構造函數將當前模塊的名稱(__name__)作為參數。2、route函數ap…

CSP認證練習題目推薦(4)

思維、貪心、綜合 排隊打水 這道題目不算難,但是不注意還是會出現很多錯誤,比如結構體的書寫。以及自定義結構體排序。還有這里做的優化,使用前綴和記錄打水的等待時間,但是這里很容易出錯的點在于等待時間是應該是記錄的前一個…

MySQL 視圖的更新與刪除:從操作規范到風險防控

MySQL 視圖的更新與刪除:從操作規范到風險防控 視圖作為 “虛擬表”,其更新與刪除操作常常讓開發者困惑 ——“為什么更新視圖會報錯?”“刪除視圖會不會弄丟數據?” 實際上,80% 的視圖操作問題都源于對 “視圖依賴基表…

C 語言實現 I.MX6ULL 點燈(續上一篇)、SDK、deep及bsp工程管理

目錄 一、匯編點燈轉 C 語言實現 1. 關鍵字:volatile 2. 寄存器地址定義(兩種方式) (1)直接宏定義地址 (2)結構體封裝寄存器(優化訪問) 3. 核心功能代碼 &#xff…

DevOps實戰(7) - 使用Arbess+GitPuk+sourcefare實現Node.js項目自動化部署

Arbess 是一款國產開源免費的 CI/CD 工具,工具支持一鍵部署,頁面簡潔易用。本文將詳細介紹如何安裝配置使用GitPuk、sourcefare、Arbess系統,使用流水線拉取GitPuk源碼、使用sourcefare代碼掃描、構建安裝包并進行主機部署。 1、GitPuk 安裝…

算法,蒜鳥蒜鳥-P1-理解“雙指針”

歡迎來到啾啾的博客🐱。 記錄學習點滴。分享工作思考和實用技巧,偶爾也分享一些雜談💬。 有很多很多不足的地方,歡迎評論交流,感謝您的閱讀和評論😄。 目錄引言1 雙指針:Two Pointers1.1 左右指…

使用cookiecutter創建python項目

一、關于Python項目結構Python 項目并沒有完全統一的 “固定結構”,但行業內有一些廣泛遵循的約定俗成的目錄結構(尤其針對可分發的包或大型項目)。同時,確實有工具可以快速生成這些標準化結構,提高開發效率&#xff0…

臺積電生態工程深度解析:從晶圓廠到蜂巢的系統架構遷移

當半導體巨頭將工廠視為生態系統,用工程思維解決環境問題概述:生態系統的工程化再造臺積電近日開展的"積蜜"項目絕非簡單的企業CSR行為,而是一場將生態系統視為復雜系統進行工程化改造的技術實踐。本文將從系統架構、數據監控、循環…

從零實現一個簡易計算器

最近在刷算法題時,遇到了實現計算器的問題。一開始覺得很簡單,但真正動手實現時才發現其中有很多細節需要考慮。今天就來分享一下我的實現思路和學到的經驗。問題分析我們需要實現一個能夠處理加減乘除四則運算的計算器,要正確處理運算符的優…

Actix-webRust Web框架入門教程

文章目錄引言Actix-web是什么?準備工作你的第一個Actix-web應用理解代碼結構處理請求和響應接收請求數據返回響應中間件 - 增強你的應用狀態管理和依賴注入實用示例:構建RESTful API測試你的Actix-web應用部署Actix-web應用結語額外資源引言 嘿&#xf…

若依框架前端通過 nginx docker 鏡像本地運行

1. 前言 項目運行過程圖:對于前端項目通過命令 npm run build 打包后,無法直接運行。存在如下錯誤:可以通過配置 nginx 服務器運行前端項目解決如上問題。 2. Nginx 運行 采用 docker 鏡像的方式運行,docker-compose.yml 文件內容…

淺聊一下HTTP協議

在日常上網瀏覽網頁、刷視頻時,背后都離不開 HTTP 協議的支持。作為 Web 世界的 “交通規則”,它負責服務器和客戶端瀏覽器之間的數據傳輸。這篇文章就帶大家全面了解 HTTP 協議,從基本概念到通信細節,再到安全相關的 HTTPS&#…

機器人控制器開發(定位——cartographer ros2 使用2)

文章總覽 1 純定位模式 當完成建圖后,會生成pbstream格式的地圖文件 配置純定位模式的lua腳本 backpack_2d_localization.lua include "backpack_2d.lua"TRAJECTORY_BUILDER.pure_localization_trimmer {max_submaps_to_keep 3, } POSE_GRAPH.optimi…

《大數據之路1》筆記3:數據管理

一 元數據 1.1 元數據概述 定義: 元數據是關于數據的數據,元數據打通了源數據、數據倉庫、數據應用,記錄了數據從生產到消費的全部過程。元數據主要記錄數據倉庫中模型的定義、各層級間的映射關系、監控數據倉庫的數據狀態和ETL的任務運行狀態…

排序實現java

排序算法概述Java中實現排序可以通過多種方式,包括內置方法、自定義算法或使用第三方庫。常見的排序算法有冒泡排序、選擇排序、插入排序、快速排序、歸并排序等。使用Arrays.sort()方法對于數組排序,Java提供了Arrays.sort()方法,支持對基本…

51c大模型~合集182

我自己的原文哦~ https://blog.51cto.com/whaosoft/14174587 #LaV-CoT 超越GPT-4o,螞蟻集團與南洋理工大學提出:首個語言感知的視覺思維鏈 隨著大型視覺語言模型(VLM)的飛速發展,它們在處理復雜的視…

C++ STL之deque的使用和模擬實現

目錄 deque 核心本質與定位 與stack和queue的關系: deque的使用 deque的底層實現 deque的原理介紹 deque的缺陷 總結: deque deque文檔 : deque 翻譯: 雙端隊列 deque(通常發音類似“deck”)是“double-ended queue”(雙端隊列&…

布草洗滌廠設備租賃押金原路退回系統—東方仙盟

設備租賃狀態設備管理添加設備設備收押金設備退押金在布草洗滌行業的運營版圖中,設備租賃是連接廠商與客戶的重要紐帶,而押金的收取與退還則是這一環節中關乎信任與效率的關鍵節點。未來之窗布草洗滌廠深諳此道,專為設備租賃業務打造的 “押金…

換源rocklinux和centos

一、Rockylinux換源,國外的源換成國內的源#nmcli connection modify ens33 ipv4.addresses 192.168.121.11 ipv4.gateway 192.168.121.2 ipv4.method manual ipv4.dns 114.114.114.114 connection.autoconnect yes修改地址#systemctl stop firewalld#systemctl diab…