看了一篇讲了chatgpt的文字输出的视频,就想记录一些 流式获取数据
- 前端部分主要是请求一个接口
let url = 'http://localhost:3000/flow';
- 进行流式数据的获取 resp.body 的 body是一个流; 流是一个抽象的概念,比方说,之前的请求方式是一口气的获取所有的,现在的这个就是慢慢的去获取,有一点数据就拿取一点数据;在流中有一个读取器 getReader。
const resp = await fetch( url,{
method:'post',
headers:{
"Content-Type":"application/json"
}
})
const reader = resp.body.getReader();
- getReader它可以使用一个函数( read )进行一部分一部分的读; 但是因为read函数读取这一块的数据也是需要时间的,所以它返回的也是一个promsie, 其中有两个属性可以拿到
done和value;
done 为true 表示读完了,没有了;
value 表示读出来的数据,读取出来的数据为类型化数组;
因为调用这个函数就会读出来一小块,所以我们放在循环里面;
调用 new TextDecoder() 的 decode 方法进行编码,然后返回字符串
const textDecoder = new TextDecoder();
while(1){
const { done,value } = await reader.read();
if(done){
break;
}
// value是一个 Uint8Array 的编码格式 是数组类型表示一个 8 位无符号整型数组
// 需要使用new TextDecoder() 的decode 进行编码
console.log(value);
const txt = textDecoder.decode(value);
// console.log(txt);
str += txt
console.log(str);
}
以下是前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>流式接口</title>
</head>
<body>
<script>
let url = 'http://localhost:3000/flow'
let str = '';
async function gertResponse(){
const resp = await fetch( url,{
method:'post',
headers:{
"Content-Type":"application/json"
}
})
const reader = resp.body.getReader();
const textDecoder = new TextDecoder();
while(1){
const { done,value } = await reader.read();
if(done){
break;
}
// value是一个 Uint8Array 的编码格式 是数组类型表示一个 8 位无符号整型数组
// 需要使用new TextDecoder() 的decode 进行编码
console.log(value);
const txt = textDecoder.decode(value);
// console.log(txt);
str += txt
console.log(str);
}
}
gertResponse();
</script>
</body>
</html>
以下是后端代码
const Koa = require('koa')
const app = new Koa()
const { PassThrough } = require("stream")
app.use(async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', '*')
ctx.set('Access-Control-Allow-Headers', 'content-type')
ctx.set('Access-Control-Allow-Methods', 'OPTIONS,GET,PUT,POST,DELETE')
await next()
})
const Router = require('koa-router');
const router = new Router();
app.use(async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', '*')
ctx.set('Access-Control-Allow-Headers', 'content-type')
ctx.set('Access-Control-Allow-Methods', 'OPTIONS,GET,PUT,POST,DELETE')
await next()
// 允许所有跨域
if (ctx.request.method === 'OPTIONS') {
console.log('跨域请求')
ctx.response.status = 200
ctx.response.message = 'OK'
}
})
// 启动路由
app.use(router.routes()).use(router.allowedMethods())
router.post('/flow', async (ctx,next) => {
ctx.request.socket.setTimeout(0);
ctx.req.socket.setNoDelay(true);
ctx.req.socket.setKeepAlive(true);
const stream = new PassThrough();
let str = '您好,请问这个人类是谁啊?为什么我不认识'.split("")
ctx.status = 200;
ctx.body = stream;
let n = 0;
setInterval(() => {
stream.write( str[n]);
n++;
if(n > str.length - 1){
n = 0;
}
}, 1000);
})
app.listen(3000)