React Native中如何基于OpenAI API实现文本流式响应?
React Native 实现OpenAI流式响应的可行方案
一、修复原生Fetch流式处理逻辑
你之前的代码仅获取了ReadableStream的reader,但未实现持续读取和解析OpenAI响应的核心逻辑。OpenAI流式响应以data: 前缀的单行文本块返回,最后以data: [DONE]标记结束,需要逐块解析并拼接内容:
import 'react-native-polyfill-globals/auto'; import { fetch } from 'react-native-fetch-api'; import { useState } from 'react'; // 组件内用useState维护流式输出内容 const [streamContent, setStreamContent] = useState(''); async function fetchStreamMessage() { try { const url = 'https://api.openai.com/v1/chat/completions'; const response = await fetch(url, { reactNative: { textStreaming: true }, method: 'POST', headers: { Authorization: `Bearer ${OPENAI_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messages: [{ role: 'user', content: 'Tell me a joke' }], model: 'gpt-4', stream: true, temperature: 0.7, }), }); if (!response.ok) throw new Error(`请求失败: ${response.status}`); const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; // 解码二进制数据为文本,保留未完整的最后一行 buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim() === '') continue; if (line.startsWith('data: ')) { const dataStr = line.slice(6); if (dataStr === '[DONE]') { reader.releaseLock(); return; } try { const data = JSON.parse(dataStr); const content = data.choices[0]?.delta?.content || ''; if (content) setStreamContent(prev => prev + content); } catch (e) { console.warn('解析响应块失败:', e); } } } } } catch (error) { console.error('流式请求错误:', error); } }
二、自行实现简化版SSE逻辑
如果第三方SSE库兼容性差,上述Fetch循环读取的方式本质就是模拟SSE的核心逻辑——持续接收服务器推送的分块数据。这个方案无需依赖额外库,兼容性更强,代码已包含在上面的示例中。
三、中转服务器方案
1. 外置中转服务器(生产环境推荐)
用Node.js搭建轻量中转服务,接收RN的请求后转发给OpenAI,再将流式响应原样返回RN,既解决兼容性问题,又能隐藏OpenAI API密钥:
// Node.js Express 中转服务器示例 const express = require('express'); const fetch = require('node-fetch'); const app = express(); app.use(express.json()); app.post('/api/chat-stream', async (req, res) => { try { const openAIRes = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.OPENAI_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ ...req.body, stream: true }), }); // 直接将OpenAI的流式响应管道到客户端 res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); openAIRes.body.pipe(res); } catch (error) { res.status(500).json({ error: error.message }); } }); app.listen(3000, () => console.log('中转服务器运行在3000端口'));
RN端只需请求该中转接口,处理流式响应的逻辑与之前一致。
2. 内置中转(Expo API Routes)
即将发布的Expo SDK API Routes允许在Expo项目内编写后端逻辑,相当于内置轻量服务器,无需额外维护外置服务:
在Expo项目的app/api/chat-stream/route.ts中编写:
import { NextResponse } from 'next/server'; import fetch from 'node-fetch'; export async function POST(request: Request) { const body = await request.json(); const openAIRes = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.EXPO_PUBLIC_OPENAI_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ ...body, stream: true }), }); return new NextResponse(openAIRes.body, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }, }); }
RN端请求地址:开发环境为http://localhost:8081/api/chat-stream,生产环境为Expo分配的域名,流式响应处理逻辑不变。
其他优化建议
- 无论采用哪种方案,都不要在RN代码中直接暴露OpenAI API密钥,中转服务器是更安全的选择;
- 添加错误重试机制,应对网络中断等异常情况;
- 若原生Fetch仍有兼容性问题,可尝试
react-native-blob-util库的流式功能,它对RN的支持更完善; - 调试时检查网络请求的响应头是否包含
Transfer-Encoding: chunked,确认服务器返回的是流式响应。
内容的提问来源于stack exchange,提问作者WanDur
相关产品推荐
相关产品推荐

