微信小程序中实现流式传输
最近公司在
All in AI
我之前负责的小程序就需要接入 deepSeek。公司其它的小程序使用的都是 webview 嵌套的 h5+浏览器原生的 fetch(流式传输)来实现的。但是在小程序中并没有 fetch 请求,因此无法使用。
什么是流式传输?
在解决问题之前,我们需要了解什么是流式传输。流式传输指的是将数据分成多个数据流,通过网络传输,以减少网络延迟和提高性能。在某些情况下,流式传输也可以用于将视频流和音频流传输到客户端。流式传输是一种高效的数据传输方式,常用于大文件下载和在线视频播放等。
解决方案
微信官方文档中提到,
wx.request
中支持onChunkReceived
分段式传输,在小程序中发起请求时,开启enableChunked
,并监听onChunkReceived
的回调,逐步处理接收到的数据块,并将处理好的字符串逐步加入到数据中。这种方式有效地应对了微信小程序请求不支持 stream 流的问题。
注意:如果使用了 uniapp,那么请使用wx.request
,而不要使用uni.request
。uniapp封装过后的流式无法处理流式数据。
代码实现
const requestTask = wx.request({
url: `${config.baseUrl}/ai/deepSeek`,
header: {
"X-Access-Token": uni.$storage.get("token"),
},
data: JSON.stringify({
question: this.question,
appIdconfig.APP_ID,
userName: 'xxx'
}),
method: "POST",
enableChunked: true, // 开启流式传输
responseType: "arraybuffer",
});
if (requestTask?.onChunkReceived) {
requestTask.onChunkReceived(this.handleChunkData);
} else {
// 如果 onChunkReceived 不存在,则使用普通的请求方式
this.normalRequest();
}
// 分块数据处理
handleChunkData(res) {
try {
let rawStr = "";
// 微信小程序专用ArrayBuffer转字符串
rawStr = wx.arrayBufferToBase64(res.data);
rawStr = this.base64ToUtf8(rawStr);
// 手动UTF-8解码,微信小程序存在兼容问题
rawStr = Array.from(new Uint8Array(res.data))
.map((byte) => String.fromCharCode(byte))
.join("");
// 中文特殊处理
rawStr = unescape(encodeURIComponent(rawStr));
const chunkJson = rawStr.split("data: ")[1];
const rawData = JSON.parse(chunkJson);
// 直接拼接有效内容
const text = rawData.choices?.[0]?.delta?.content;
// 移除think部分的内容,通过</think>来做标识
if (text == "</think>") {
this.isThink = false;
}
// 根据自己的业务来做处理
if (!this.isThink) {
const tx = text.replace("</think>", "");
this.streamBuffer += tx;
// 实时更新最新内容(自动Markdown转换)
const answer = this.list[this.list.length - 1];
let render = markdown.render(this.streamBuffer);
answer.content = render;
// 优化性能的节流更新
this.throttleUpdate();
}
} catch (e) {
console.error("数据处理异常:", e);
}
}
// 辅助函数,根据项目内容自己调整
// 之前使用的textDocode,但是微信小程序存在兼容问题,所以改用ArrayBuffer转字符串
base64ToUtf8(base64Str) {
return this.wechatBase64Decode(base64Str);
},
wechatBase64Decode(base64Str) {
const arr = wx.base64ToArrayBuffer(base64Str);
return this.arrayBufferToString(arr);
},
browserBase64Decode(base64Str) {
const binaryStr = atob(base64Str);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
return this.arrayBufferToString(bytes.buffer);
},
arrayBufferToString(buffer) {
try {
return new TextDecoder("utf-8").decode(buffer);
} catch (e) {
let str = "";
new Uint8Array(buffer).forEach((byte) => {
str += String.fromCharCode(byte);
});
return decodeURIComponent(escape(str));
}
},
// // 节流更新
throttleUpdate() {
if (!this.updateTimer) {
this.updateTimer = setTimeout(() => {
this.$forceUpdate();
this.scrollToBottom();
this.updateTimer = null;
}, 500);
}
}
要点 1: 将 enableChunked 设置为 true
要点 2: 监听 onChunkReceived 的回调
要点 3: 使用markdown-it解析数据
待优化项
- 由于小程序没有提供
onReceivedEnd
事件,因此无法得知当前消息是否发送完毕,只能通过返回返回字段中的特殊标识符来判断当前是否已发送完毕(需要和后端约束)。以此来动态设置当前这一则消息的loading加载状态。 - deepSeek深度思考返回的结果中包含了大量的think内容,对于当前场景中,并不需要展示think部分的内容,做一部分内容筛减,只有当匹配到think结束符时才向stringBuffer中填充文本内容。
- markdown-it在解析时需要确保文内内容符合解析规则,根据返回结果动态调整内容,否则页面可能无法展示。
结束语
通过在请求中设置enableChunked: true
和监听onChunkReceived
回调,解决了微信小程序中不支持 stream 流的流式传输问题。
以上,就解决了我们的流式传输问题,后面会分享在h5中实现流式数据处理的方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具