微信小程序对接SSE接口记录
- 需求:公司项目对接gpt,gpt产生的结果是分段返回,所以要求在产生结果时,有打字机的效果。原本是由定时器调用,后来优化改为服务端使用SSE接口。
- 小程序使用起来比较方便,但是要求小程序基本库 的版本需要在2.20.2 以上。文档地址移步这里
- 微信小程序代码
javascript
// 基础库为2.33.0
const requestTask = wx.request({
url: `xxxxxxxx`, // 需要请求的接口地址
enableChunked: true // enableChunked必须为true
})
// 开发工具存在问题,使用真机测试
const listener = data => {
// data为返回的数据,可以在此对数据进行处理
}
// 监听服务端返回的数据
requestTask.onChunkReceived(listener)
// 移除监听 需传入与监听时同一个的函数对象
requestTask.offChunkReceived(listener)
注意:
- 接收到的结果数据类型 固定为arrayBuffer,需要开发者自己进行转换,可以使用下面方法进行转换。
javascript
function arrayBufferToString(arr){
if(typeof arr === 'string') {
return arr;
}
var dataview = new DataView(arr);
var ints = new Uint8Array(arr.byteLength);
for(var i=0;i<ints.length;i++){
ints[i]=dataview.getUint8(i);
}
var str = '',
_arr = ints;
for(var i = 0; i < _arr.length; i++) {
if (_arr[i]) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
if ( _arr[st + i]) {
store += _arr[st + i].toString(2).slice(2);
}
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
}
return str;
}
- 微信开发工具中无法转换数据。可能是由于开发工具问题,在服务端返回的字符串中存在中文时,开发工具是无法正常转换的。但是在真机是正常的。如果需要在开发工具中实现转换,可以与服务端协商将数据进行URL编码返回。
- 服务端一次返回的结果,微信小程序有时会将其截开,并分两次返回。由于截开的位置并不固定,所以可能会存在转换ArrayBuffer时,出现结果异常的问题。使用SSE接口一般有两种需求:一种是将所有的结果累加起来、还有一种就是后面的结果覆盖前面的。在使用第一种时,每次的返回量不会太大,所以应该不会出现微信小程序截开两次返回的情况。但是第二种每次返回的接口都在逐渐增大,可能会出现这种情况,我就是第二种。我是使用下面方法解决
javascript
// 我的数据是json字符串,如果出现分开返回,在转json时,会出现报错,所以使用try处理
let timer = null
const listener = data => {
try {
// 上次结果出现报错 这次正常 清除延时器
if (timer) {
clearTimeout(timer)
timer = null
}
// 小程序存在数据截开的情况 存五次数据
if (arr.length > 4) {
arr.shift()
}
// 这里要存储的是arrayBuffer,不能存储string数据
arr.push(data.data)
// 数据处理 .......
} catch (e) {
// 最后一次出现报错 三秒后重组数据
timer = setTimeout(() => {
const len = arr.length
let index = len - 2,
data = arr[len - 1],
result = null
while(index > -1) {
// 从后往前 合并
data = mergeArrayBuffers(arr[index], data)
try{
// 数据处理 .......
index = -1
}catch(e){
index -= 1
}
}
}, 3000)
}
}
javascript
// 合并arrayBuffer
function mergeArrayBuffers(buffer1, buffer2) {
if (!buffer1) {
return buffer2;
} else if (!buffer2) {
return buffer1;
}
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
}
- 由于是后面的结果覆盖前面的,我只需要处理最后一次结果,如果结果正常则不用处理。不正常再将前面存储的数据一一合并,再做处理。
- 通常一个中文是两个字节,所以可能会出现一个中文恰好被截开的情况,所以需要存储的是原数据
- 由于SSE特性,需要由用户端断开连接,所以在使用完毕时,需要调用
requestTask.abort()
断开连接
ps: 此文章做个人平常记录