已经很久没有来过这里热闹了,也为了工资而努力工作,最后却无缘无故。 如果你要问我是谁,努力的终结者。
最近在指导群里小兄弟的技术问题,发现了一个对我来说比较难的问题。 于是我激发了自己潜意识的精神力量——到底。 由于最近沉浸其中,已经很久没有用百度搜索东西了。 正如我所料,他们在这方面没有解决办法。 所以,记录一下探索研究的过程,给各位水友一个分享延伸。
小程序上丢包传输的实现
模拟效果实现流式传输,通过处理流式数据实现打字机的效果。
什么是流媒体?
在解决问题之前,我们需要先了解什么是流。 流式传输是指将数据分成多个数据流通过网络传输,以减少网络延迟并提高性能。 在某些情况下,流式传输还可以用于将视频和音频流式传输到客户端。 流式传输是一种高效的数据传输方式,常用于大文件下载、在线视频播放等场景。
为什么小程序不支持流式传输?
小程序当前不支持流式传输,尽管流式传输在某些情况下可能很有用。 主要原因是小程序的架构和限制。
小程序开发框架提供了小程序的开发和调试环境。 在这种环境下,小程序的代码和资源被打包在一个文件中。 这意味着小程序依赖于这个框架的环境,无法调用浏览器的标准API,所以需要框架的整体升级和支持。
另外,小程序对网络请求的限制也是一个因素。 小程序中的网络请求必须使用微信提供的API,而这些API通常只支持完整的请求和响应。 这使得小程序无法使用流式传输。
我的解决方案
但这些都是常规方案,实现起来比较麻烦,使得正常的请求变得复杂。 放弃~
常规计划
基本的html模式就不列出来了,比较方便,我有信心这个方案是可行的。
重点:
request({ url: '/api/prompt', //请求头需要改为stream模式 headers: { Accept: 'text/event-stream', }, //响应类型设置stream responseType: 'stream', method: 'POST', data: { prompt: prompt, }, }).then(res => { console.log(res) }).catch(err => { console.log(err) })复制代码
他们要求我做一个打字机效果,我的计划是:收到数据后对其进行解码。
.then((res) => { const arrayBuffer = res.data; const uint8Array = new Uint8Array(arrayBuffer); const textDecoder = new TextDecoder('utf-8'); const text = textDecoder.decode(uint8Array); for (let i = 0; i < text.length; i++) { setTimeout(() => { resultText += text[i]; console.log(resultText); }, i * 100); }})复制代码
ok,浏览器没问题开发一个类似微信的小程序,小程序调试工具也没问题,我对自己的解决方案还是有信心的
但是小程序报错,无法打印流数据,无法支持该方法。 小牛之后,顾问就成了盲人指挥。
另一种方法:解决方案
微信官方文档中提到,wx中支持分段传输。
重点:
const requestTask = wx.request({ url: '/api/prompt', //请求头需要改为stream模式 header: { "Transfer-Encoding": 'chunked' }, timeout: 15000, responseType: 'text', method: 'POST', enableChunked: true, data: { prompt: prompt, }, }).then(res => { console.log(res) }).catch(err => { console.log(err) })复制代码
这样我们就发起了流式请求,当然后端也需要支持,后面我会举例。
当他们这样做时,又出了问题,他们找不到替代方案。 我查了一下,好像有计划,我没有信心。 使用“”代替库,然后给出建议:
import {TextEncoder, TextDecoder} from "fastestsmallesttextencoderdecoder";const encode = (new TextEncoder).encode;const decode = (new TextDecoder).decode;复制代码
等了一天也不找我,哼,小菜一碟,搞定了。
又来了,大哥,你的方法不行,引入执行又报错。
不要惊慌...尝试手写传输解决方案:text-并亲自测试一下,如果不确定,请关闭-plus。
最终版本:
let buffer=''requestTask.onChunkReceived(function (response) { const arrayBuffer = response.data; const uint8Array = new Uint8Array(arrayBuffer); let text = String.fromCharCode.apply(null, uint8Array); buffer += text; full_command.value = buffer })复制代码
其实第二种方案是可行的,但是我没时间看报什么错误。 最后用的方法可以处理得恰到好处,当然还有一些过滤和解码,根据业务需求来写。
后台接口配置
后端配置教程很多,主要是添加请求头,支持分段传输。
public static function prompt($message) { $openAi = self::getOpenAI(); header('Access-Control-Allow-Credentials: true'); // 设置响应头信息 header('Transfer-Encoding: chunked'); header('Content-Type: text/plain'); header('Cache-Control: no-cache'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type'); header('Connection: keep-alive'); $msg = ""; $openAi->prompt([ 'messages' => $message, 'model' => 'gpt-3.5-turbo', "stream" => true, ], function ($curl_info, $response) { //闭包函数处理流 $data = []; $lines = explode("\n", $response); foreach ($lines as $line) { if (!str_contains($line, ':')) { continue; } [$name, $value] = explode(':', $line, 2); if ($name == 'data') { $data[] = trim($value); } } foreach ($data as $message) { if ('[DONE]' === $message) { echo "0\r\n\r\n"; } else { $message = json_decode($message, true); $input = $message['choices'][0]['delta']['content'] ?? ''; $msg .= $input; echo dechex(strlen($msg)) . "\r\n" . $msg . "\r\n"; } } ob_flush(); flush(); return strlen($response); }); }复制代码
至此整个浏览完毕,相信有技术感的朋友会有很大的用处。 目前我们还没有看到太多支持流替换的小程序。 至少把md格式、代码高亮、打字效果处理成和官网一样的交互,还是挺有技巧的。 不过你可以试试这个,我用得很好,至少是交互式的。 稍后我会发一个整合所有替代品的分享,让大家可以嫖到老。
关注我,私信我,加入我的技术交流群,一起畅游技术世界吧~
本文为参与“金石计划”