如何从Next.js Server Action向客户端组件传递流?
解决Next.js Server Action向客户端传递流的问题
Server Action无法直接返回ReadableStream对象,因为Next.js要求Server Action返回的内容必须是可序列化的普通对象。以下是两种可行的解决方法:
方法一:返回AsyncIterable(推荐)
修改Server Action,将ReadableStream转换为AsyncIterable,客户端通过for await...of迭代消费流内容:
1. 更新Server Action(actions.ts)
export async function* getSuggestion(): AsyncGenerator<string, void, unknown> { try { // 原有逻辑:获取session等 const session = ...; const stream = await GPT.getAISuggestion(session); const reader = stream.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; // 逐块解码并返回文本片段 yield decoder.decode(value, { stream: true }); } } catch (error) { console.error(error); throw new Error("获取AI建议失败"); } }
2. 客户端组件调用(ClientComponent.tsx)
"use client"; // ... 其他逻辑 async function fetchSuggestion() { try { let text = ""; // 迭代AsyncIterable获取流内容 for await (const chunk of getSuggestion()) { text += chunk; setAiSuggestionsText(text); } } catch (error) { setError(error instanceof Error ? error.message : "未知错误"); } }
方法二:通过API路由中转(若需保留ReadableStream)
如果必须使用ReadableStream,可以创建一个API路由来返回流,Server Action仅返回该API的URL,客户端通过fetch获取流:
1. 创建API路由(app/api/ai-suggestion/route.ts)
import { NextResponse } from "next/server"; import { getAISuggestion } from "@/lib/gpt"; export async function GET(request: Request) { // 原有逻辑:获取session等 const session = ...; const stream = await getAISuggestion(session); return new NextResponse(stream, { headers: { "Content-Type": "text/plain; charset=utf-8", "Transfer-Encoding": "chunked", }, }); }
2. 更新Server Action(actions.ts)
export async function getSuggestion() { try { // 返回API路由的URL return { data: "/api/ai-suggestion" }; } catch (error) { console.error(error); return { error: "获取AI建议失败" }; } }
3. 客户端组件调用(ClientComponent.tsx)
"use client"; // ... 其他逻辑 async function fetchSuggestion() { const { data, error } = await getSuggestion(); if (error) { setError(error); return; } try { const response = await fetch(data); if (!response.body) return; const reader = response.body.getReader(); const decoder = new TextDecoder(); let text = ""; let done = false; while (!done) { const { value, done: readerDone } = await reader.read(); done = readerDone; if (value) { text += decoder.decode(value, { stream: true }); setAiSuggestionsText(text); } } } catch (err) { setError("获取流内容失败"); } }
内容的提问来源于stack exchange,提问作者Florian Walther
相关产品推荐
相关产品推荐

