后端:
def query():
#response是生成的一个结果
# 创建一个生成器函数,用于逐个生成JSON对象
results=response.split("\n")
def generate():
# 遍历搜索结果
for response in results:
# 创建一个部分响应的字典
part_response = {
'message': response+"\n",
}
# 将字典转换为JSON字符串,并确保每个JSON对象都是独立的
yield (json.dumps(part_response, ensure_ascii=False) + '\n\n').encode('utf-8')
time.sleep(1) # 模拟延迟
return Response(stream_with_context(generate()), content_type='text/event-stream')
query()
函数定义了一个生成器函数generate
,它将一个名为response
的字符串分割成多行,并将每一行作为一个单独的JSON对象逐个生成。response.split("\n")
将response
字符串按换行符\n
分割成一个列表results
。generate()
函数是一个生成器,它遍历results
列表中的每个元素(即每行文本)。对于
results
中的每一行,代码创建一个包含这一行文本的字典part_response
。json.dumps(part_response, ensure_ascii=False)
将字典转换为JSON格式的字符串。ensure_ascii=False
允许JSON中包含非ASCII字符。yield (json.dumps(part_response, ensure_ascii=False) + '\n\n').encode('utf-8')
这行代码将每个JSON对象作为字节字符串生成,并在对象之间添加两个换行符,以符合SSE的格式要求。time.sleep(1)
模拟延迟,每生成一个JSON对象后,函数将暂停1秒钟。这在实际应用中可能用于模拟实时数据推送的延迟。Response(stream_with_context(generate()), content_type='text/event-stream')
创建了一个Django响应对象,它将generate()
生成器作为流来处理。stream_with_context
是一个辅助函数,它确保在生成器运行时,Django的请求上下文是激活的。content_type='text/event-stream'
指定了响应的MIME类型,这样客户端浏览器会识别这是一个SSE响应。
前端代码:
// 使用 fetch API 发送 POST 请求
fetch(url, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
responseType: 'json', // 设置响应类型为 json
})
.then((response) => {
// 检查响应状态
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.body // 获取流式响应
})
.then((stream) => {
// 使用 ReadableStream 来处理流式数据
const reader = stream.getReader()
const decoder = new TextDecoder('utf-8')
// 使用箭头函数来定义 read 函数,确保正确的 this 上下文
const read = () => {
return reader.read().then((result) => {
if (result.done) {
console.log('Stream complete')
return
}
const chunk = decoder.decode(result.value, { stream: true })
console.log(`Received chunk: ${chunk}`)
// 更新前端内容
this.content += JSON.parse(chunk).message
if (this.searchResults == '') {
this.searchResults = JSON.parse(chunk).searchResults
}
this.loading = false
// 如果需要,可以在这里处理数据,更新界面等
return read() // 继续读取下一块数据
})
}
read().catch((error) => {
console.error('Error occurred while reading stream:', error)
})
})
.catch((error) => {
console.error('Error during POST request:', error)
this.loading = false
})
读取流式数据:
const reader = stream.getReader()
从响应流中获取一个读取器。const decoder = new TextDecoder('utf-8')
创建一个文本解码器,用于将流中的二进制数据解码为文本。const read = () => {...}
定义一个递归函数read
,用于连续读取流中的数据块。reader.read()
从流中读取下一个数据块。- 如果读取完成(
result.done
为true
),则打印消息并结束。 - 否则,使用
decoder.decode()
将数据块解码为文本,并打印接收到的数据块。 this.content += JSON.parse(chunk).message
将解码后的消息更新到Vue组件的数据属性content
中。- 如果
this.searchResults
为空,则将其更新为从流中解析出的搜索结果。 this.loading = false
更新Vue组件的数据属性loading
,可能用于表示加载状态。