java調(diào)用第三方接口實(shí)現(xiàn)流式輸出的示例代碼
源代碼
@Operation(summary = "問答問題查詢接口")
@PostMapping(value = "/question/answer")
public String questionAnswer(@Parameter(description = "用戶問題", required = true) @RequestParam("message") String message,
@Parameter(description = "Admin-Token", required = true) @RequestHeader( required = false) String token) throws IOException {
return projectService.questionAnswer(message, token);
}
public String questionAnswer(String message,String token){
String chatNormalResult = this.chatNormal(message,chatModel);
return chatNormalResult;
}
public String chatNormal(String message,String modelName){
try {
long startTime = System.nanoTime();
ObjectMapper objectMapper = new ObjectMapper();
// 調(diào)用chatNormal做數(shù)據(jù)分析
String chatNormalUrl = "http://localhost:5670/api/v2/chat/completions";
Map<String, Object> chatNormalBody = new HashMap<>();
// 設(shè)置請求頭
Map<String, String> headers = new HashMap<>();
headers.put("accept", "application/json");
headers.put("Content-Type", "application/json");
chatNormalBody.put("messages",message);
chatNormalBody.put("model",modelName);
chatNormalBody.put("stream",true);
chatNormalBody.put("temperature",0.5);
chatNormalBody.put("max_new_tokens",4000);
chatNormalBody.put("conv_uid", UUID.randomUUID().toString());
String chatNormalBodyJson = objectMapper.writeValueAsString(chatNormalBody);
// 發(fā)送POST請求
long startTime2 = System.nanoTime();
String resultData = okHttpUtil.postJson(chatNormalUrl, chatNormalBodyJson, headers);
return resultData;
}catch (Exception e){
return "暫無數(shù)據(jù)";
}
}
基于原有代碼的最小改動(dòng)方案
1. 首先,修改pom.xml添加必要依賴
<!-- 如果還沒有OkHttp,添加這個(gè)依賴 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
2. 修改Controller接口(增加流式接口,原接口不變)
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@Operation(summary = "問答問題查詢接口(流式)")
@PostMapping(value = "/question/answer/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public void questionAnswerStream(
@Parameter(description = "用戶問題", required = true) @RequestParam("message") String message,
@Parameter(description = "Admin-Token", required = true) @RequestHeader(required = false) String token,
HttpServletResponse response) throws IOException {
projectService.questionAnswerStream(message, token, response);
}
// 原有的同步接口保持不變
@Operation(summary = "問答問題查詢接口")
@PostMapping(value = "/question/answer")
public String questionAnswer(
@Parameter(description = "用戶問題", required = true) @RequestParam("message") String message,
@Parameter(description = "Admin-Token", required = true) @RequestHeader(required = false) String token) throws IOException {
return projectService.questionAnswer(message, token);
}
3. 修改Service方法(最簡單的方式)
import okhttp3.*;
import com.fasterxml.jackson.databind.ObjectMapper;
public void questionAnswerStream(String message, String token, HttpServletResponse response) throws IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Connection", "keep-alive");
PrintWriter writer = response.getWriter();
try {
ObjectMapper objectMapper = new ObjectMapper();
String chatNormalUrl = "http://localhost:5670/api/v2/chat/completions";
// 構(gòu)建請求體
Map<String, Object> requestBody = new HashMap<>();
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
requestBody.put("messages", new Map[]{userMessage});
requestBody.put("model", "deepseek-ai/DeepSeek-R1");
requestBody.put("stream", true); // 這里改為true
requestBody.put("temperature", 0.5);
requestBody.put("max_new_tokens", 4000);
requestBody.put("conv_uid", UUID.randomUUID().toString());
String requestBodyJson = objectMapper.writeValueAsString(requestBody);
// 使用OkHttp進(jìn)行流式調(diào)用
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(chatNormalUrl)
.post(RequestBody.create(requestBodyJson, MediaType.parse("application/json")))
.addHeader("accept", "application/json")
.addHeader("Content-Type", "application/json")
.build();
// 關(guān)鍵:使用流式響應(yīng)
Response apiResponse = client.newCall(request).execute();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(apiResponse.body().byteStream()))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
// 直接轉(zhuǎn)發(fā)給前端
writer.write(line + "\n\n");
writer.flush();
}
}
}
} catch (Exception e) {
// 發(fā)送錯(cuò)誤信息
writer.write("data: {\"error\": \"" + e.getMessage() + "\"}\n\n");
writer.flush();
e.printStackTrace();
}
}
// 原有的同步方法保持不變
public String questionAnswer(String message, String token) {
try {
// 這里可以調(diào)用一個(gè)新的方法,或者復(fù)用部分邏輯,但stream=false
return callChatAPI(message, false);
} catch (Exception e) {
return "請求失敗: " + e.getMessage();
}
}
// 如果想把流式和非流式邏輯統(tǒng)一,可以這樣重構(gòu)
private String callChatAPI(String message, boolean isStream) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String chatNormalUrl = "http://localhost:5670/api/v2/chat/completions";
Map<String, Object> requestBody = new HashMap<>();
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
requestBody.put("messages", new Map[]{userMessage});
requestBody.put("model", "deepseek-ai/DeepSeek-R1");
requestBody.put("stream", isStream); // 根據(jù)參數(shù)決定是否流式
requestBody.put("temperature", 0.5);
requestBody.put("max_new_tokens", 4000);
requestBody.put("conv_uid", UUID.randomUUID().toString());
String requestBodyJson = objectMapper.writeValueAsString(requestBody);
Map<String, String> headers = new HashMap<>();
headers.put("accept", "application/json");
headers.put("Content-Type", "application/json");
if (isStream) {
// 流式邏輯 - 這里需要不同的處理,但暫時(shí)不實(shí)現(xiàn)
return "流式調(diào)用需使用特定方法";
} else {
// 非流式邏輯
String resultData = okHttpUtil.postJson(chatNormalUrl, requestBodyJson, headers);
// 解析結(jié)果
Map<String, Object> result = objectMapper.readValue(resultData, Map.class);
List<Map<String, Object>> choices = (List<Map<String, Object>>) result.get("choices");
if (choices != null && !choices.isEmpty()) {
Map<String, Object> messageObj = (Map<String, Object>) choices.get(0).get("message");
return (String) messageObj.get("content");
}
return "暫無數(shù)據(jù)";
}
}
4. 如果不想用OkHttp,想保留原有的okHttpUtil,可以用這種方式
public void questionAnswerStream(String message, String token, HttpServletResponse response) throws IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Connection", "keep-alive");
PrintWriter writer = response.getWriter();
try {
// 使用Socket直接連接,這是最直接的流式處理方式
Socket socket = new Socket("localhost", 5670);
// 構(gòu)建HTTP請求
String requestBody = buildRequestBody(message);
String httpRequest =
"POST /api/v2/chat/completions HTTP/1.1\r\n" +
"Host: localhost:5670\r\n" +
"Accept: application/json\r\n" +
"Content-Type: application/json\r\n" +
"Content-Length: " + requestBody.length() + "\r\n" +
"Connection: close\r\n" +
"\r\n" +
requestBody;
// 發(fā)送請求
OutputStream out = socket.getOutputStream();
out.write(httpRequest.getBytes(StandardCharsets.UTF_8));
out.flush();
// 讀取響應(yīng)
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 跳過HTTP響應(yīng)頭
String line;
while ((line = reader.readLine()) != null && !line.isEmpty()) {
// 跳過頭部
}
// 讀取SSE數(shù)據(jù)
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
writer.write(line + "\n\n");
writer.flush();
if (line.equals("data: [DONE]")) {
break;
}
}
}
socket.close();
} catch (Exception e) {
writer.write("data: {\"error\": \"" + e.getMessage() + "\"}\n\n");
writer.flush();
e.printStackTrace();
}
}
private String buildRequestBody(String message) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> requestBody = new HashMap<>();
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
requestBody.put("messages", new Map[]{userMessage});
requestBody.put("model", "deepseek-ai/DeepSeek-R1");
requestBody.put("stream", true);
requestBody.put("temperature", 0.5);
requestBody.put("max_new_tokens", 4000);
requestBody.put("conv_uid", UUID.randomUUID().toString());
return objectMapper.writeValueAsString(requestBody);
}
5. 最簡單的測試版本(先驗(yàn)證能否流式)
public void questionAnswerStream(String message, String token, HttpServletResponse response) throws IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
// 先模擬流式輸出,驗(yàn)證前端能收到
writer.write("data: {\"choices\":[{\"delta\":{\"content\":\"正在處理您的問題...\\n\"}}]}\n\n");
writer.flush();
Thread.sleep(1000);
writer.write("data: {\"choices\":[{\"delta\":{\"content\":\"這是流式輸出的第一段內(nèi)容。\\n\"}}]}\n\n");
writer.flush();
Thread.sleep(1000);
writer.write("data: {\"choices\":[{\"delta\":{\"content\":\"這是第二段內(nèi)容。\\n\"}}]}\n\n");
writer.flush();
Thread.sleep(1000);
writer.write("data: [DONE]\n\n");
writer.flush();
}
6. 前端調(diào)用(最簡單的HTML頁面)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>測試流式輸出</title>
</head>
<body>
<h2>測試流式問答</h2>
<input type="text" id="question" placeholder="輸入問題">
<button onclick="askQuestion()">提問</button>
<div id="answer" style="border:1px solid #ccc; padding:10px; margin-top:10px; min-height:100px;"></div>
<script>
function askQuestion() {
const question = document.getElementById('question').value;
const answerDiv = document.getElementById('answer');
answerDiv.innerHTML = '';
// 使用EventSource接收流式數(shù)據(jù)
const eventSource = new EventSource(`/api/question/answer/stream?message=${encodeURIComponent(question)}`);
eventSource.onmessage = function(event) {
const data = event.data;
if (data === '[DONE]') {
eventSource.close();
return;
}
try {
const json = JSON.parse(data);
if (json.choices && json.choices[0].delta && json.choices[0].delta.content) {
answerDiv.innerHTML += json.choices[0].delta.content;
}
} catch (e) {
console.error('解析錯(cuò)誤:', e);
}
};
eventSource.onerror = function(error) {
console.error('連接錯(cuò)誤:', error);
eventSource.close();
};
}
</script>
</body>
</html>
關(guān)鍵改動(dòng)總結(jié):
- 新增一個(gè)流式接口:使用
produces = MediaType.TEXT_EVENT_STREAM_VALUE - 修改HttpServletResponse:設(shè)置SSE相關(guān)響應(yīng)頭
- 保持原有接口不變:不影響現(xiàn)有調(diào)用
- 使用PrintWriter流式輸出:最簡單的流式輸出方式
- 前端使用EventSource接收:最簡潔的前端實(shí)現(xiàn)
如果希望更簡單地集成,這里是最簡單的方案:
@PostMapping(value = "/question/answer/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public void questionAnswerStream(
@RequestParam("message") String message,
@RequestHeader(required = false) String token,
HttpServletResponse response) throws IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
// 直接調(diào)用外部命令(如果API支持命令行調(diào)用)
try {
// 構(gòu)建curl命令調(diào)用DeepSeek API
String[] cmd = {
"curl", "-X", "POST",
"http://localhost:5670/api/v2/chat/completions",
"-H", "Content-Type: application/json",
"-H", "accept: application/json",
"-d", String.format("{\"messages\":[{\"role\":\"user\",\"content\":\"%s\"}],\"model\":\"deepseek-ai/DeepSeek-R1\",\"stream\":true}", message)
};
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
writer.write(line + "\n\n");
writer.flush();
}
process.waitFor();
} catch (Exception e) {
writer.write("data: {\"error\": \"" + e.getMessage() + "\"}\n\n");
writer.flush();
}
}
只需要新增一個(gè)接口,就可以實(shí)現(xiàn)最直接的流式輸出方式
集成到現(xiàn)有OkHttpUtil工具類的流式輸出方案
作為資深專家,我會(huì)充分復(fù)用現(xiàn)有的工具類。以下是基于現(xiàn)有代碼的最小改動(dòng)方案:
1. 首先,在OkHttpUtil中添加流式處理方法
@Slf4j
@Component
public class OkHttpUtil {
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.MINUTES)
.writeTimeout(30, TimeUnit.MINUTES)
.callTimeout(90, TimeUnit.MINUTES)
.build();
/**
* 流式POST請求 - 專為SSE設(shè)計(jì)
*/
public void postJsonStream(String url, String json, Map<String, String> headers,
Consumer<String> lineConsumer) throws IOException {
Request.Builder builder = new Request.Builder()
.url(url)
.post(RequestBody.create(json, okhttp3.MediaType.parse("application/json")));
if (headers != null) {
headers.forEach(builder::addHeader);
}
Request request = builder.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("請求失敗: " + response.code());
}
ResponseBody body = response.body();
if (body == null) {
return;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(body.byteStream()))) {
String line;
while ((line = reader.readLine()) != null) {
lineConsumer.accept(line);
}
}
}
}
// 原有的同步方法保持不變
public String postJson(String url, String json, Map<String, String> headers) throws IOException {
// ... 原有的實(shí)現(xiàn) ...
return null;
}
}
2. 修改Service方法(最小改動(dòng))
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
public void questionAnswerStream(String message, String token, HttpServletResponse response) throws IOException {
// 設(shè)置SSE響應(yīng)頭
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Connection", "keep-alive");
PrintWriter writer = response.getWriter();
try {
// 復(fù)用原有的請求構(gòu)建邏輯
ObjectMapper objectMapper = new ObjectMapper();
// 構(gòu)建請求體 - 和原有代碼一模一樣,只是stream=true
Map<String, Object> chatNormalBody = new HashMap<>();
// 構(gòu)建messages - 注意這里需要是數(shù)組
List<Map<String, String>> messages = new ArrayList<>();
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
messages.add(userMessage);
chatNormalBody.put("messages", messages);
chatNormalBody.put("model", "deepseek-ai/DeepSeek-R1");
chatNormalBody.put("stream", true); // 關(guān)鍵:改為true
chatNormalBody.put("temperature", 0.5);
chatNormalBody.put("max_new_tokens", 4000);
chatNormalBody.put("conv_uid", UUID.randomUUID().toString());
String chatNormalBodyJson = objectMapper.writeValueAsString(chatNormalBody);
// 構(gòu)建請求頭
Map<String, String> headers = new HashMap<>();
headers.put("accept", "application/json");
headers.put("Content-Type", "application/json");
// 關(guān)鍵:使用新的流式方法調(diào)用
okHttpUtil.postJsonStream(
"http://localhost:5670/api/v2/chat/completions",
chatNormalBodyJson,
headers,
line -> {
// 處理每一行流式數(shù)據(jù)
if (line.startsWith("data: ")) {
try {
writer.write(line + "\n\n");
writer.flush();
// 如果收到結(jié)束標(biāo)志,可以在這里做額外處理
if (line.equals("data: [DONE]")) {
log.info("流式輸出完成");
}
} catch (Exception e) {
log.error("寫入響應(yīng)失敗", e);
}
}
}
);
} catch (Exception e) {
log.error("流式請求失敗", e);
// 發(fā)送錯(cuò)誤信息給前端
try {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
String errorJson = objectMapper.writeValueAsString(error);
writer.write("data: " + errorJson + "\n\n");
writer.flush();
} catch (Exception ex) {
// 忽略
}
}
}
// 原有的同步方法完全保持不變
public String questionAnswer(String message, String token) {
String chatNormalResult = this.chatNormal(message, "deepseek-ai/DeepSeek-R1");
return chatNormalResult;
}
// 原有的chatNormal方法完全保持不變
public String chatNormal(String message, String modelName) {
try {
long startTime = System.nanoTime();
ObjectMapper objectMapper = new ObjectMapper();
String chatNormalUrl = "http://localhost:5670/api/v2/chat/completions";
Map<String, Object> chatNormalBody = new HashMap<>();
Map<String, String> headers = new HashMap<>();
headers.put("accept", "application/json");
headers.put("Content-Type", "application/json");
// 注意:這里messages應(yīng)該是數(shù)組,看原有代碼怎么寫的
// 假設(shè)原有代碼已經(jīng)處理好了
chatNormalBody.put("messages", message); // 這里可能需要調(diào)整
chatNormalBody.put("model", modelName);
chatNormalBody.put("stream", false); // 同步請求不流式
chatNormalBody.put("temperature", 0.5);
chatNormalBody.put("max_new_tokens", 4000);
chatNormalBody.put("conv_uid", UUID.randomUUID().toString());
String chatNormalBodyJson = objectMapper.writeValueAsString(chatNormalBody);
String resultData = okHttpUtil.postJson(chatNormalUrl, chatNormalBodyJson, headers);
return resultData;
} catch (Exception e) {
return "暫無數(shù)據(jù)";
}
}
3. 或者,更簡單的方式 - 直接修改chatNormal方法支持流式
如果想讓代碼更統(tǒng)一,可以這樣修改:
// 在Service類中添加這個(gè)方法
public void chatNormalStream(String message, String modelName, PrintWriter writer) {
try {
ObjectMapper objectMapper = new ObjectMapper();
// 構(gòu)建messages
List<Map<String, String>> messages = new ArrayList<>();
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
messages.add(userMessage);
// 構(gòu)建請求體
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("messages", messages);
requestBody.put("model", modelName);
requestBody.put("stream", true);
requestBody.put("temperature", 0.5);
requestBody.put("max_new_tokens", 4000);
requestBody.put("conv_uid", UUID.randomUUID().toString());
String jsonBody = objectMapper.writeValueAsString(requestBody);
// 請求頭
Map<String, String> headers = new HashMap<>();
headers.put("accept", "application/json");
headers.put("Content-Type", "application/json");
// 使用OkHttp直接調(diào)用(不通過工具類,因?yàn)樾枰魇教幚恚?
OkHttpClient client = new OkHttpClient();
okhttp3.Request request = new okhttp3.Request.Builder()
.url("http://localhost:5670/api/v2/chat/completions")
.post(okhttp3.RequestBody.create(
jsonBody,
okhttp3.MediaType.parse("application/json")
))
.headers(okhttp3.Headers.of(headers))
.build();
try (okhttp3.Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
ResponseBody body = response.body();
if (body != null) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(body.byteStream())
);
String line;
while ((line = reader.readLine()) != null) {
// 直接轉(zhuǎn)發(fā)SSE格式數(shù)據(jù)
if (line.startsWith("data: ")) {
writer.write(line + "\n\n");
writer.flush();
}
}
}
}
}
} catch (Exception e) {
log.error("流式請求失敗", e);
try {
writer.write("data: {\"error\": \"" + e.getMessage() + "\"}\n\n");
writer.flush();
} catch (Exception ex) {
// 忽略
}
}
}
4. 最簡化的Controller調(diào)用
@Operation(summary = "問答問題查詢接口 - 流式")
@PostMapping(value = "/question/answer/stream", produces = "text/event-stream;charset=UTF-8")
public void questionAnswerStream(
@RequestParam("message") String message,
@RequestHeader(required = false) String token,
HttpServletResponse response) throws IOException {
// 設(shè)置SSE響應(yīng)頭
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Connection", "keep-alive");
// 直接調(diào)用
projectService.chatNormalStream(message, "deepseek-ai/DeepSeek-R1", response.getWriter());
}
5. 如果不想修改OkHttpUtil,最簡單的版本
// 直接在Service中處理流式請求
public void questionAnswerStream(String message, String token, HttpServletResponse response) throws IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
try {
// 復(fù)用原有的OkHttpClient配置
OkHttpClient client = OkHttpUtil.getClient(); // 如果client是public或提供getter
// 構(gòu)建請求體(和原有代碼一致)
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> requestBody = new HashMap<>();
// 構(gòu)建messages數(shù)組
List<Map<String, String>> messages = new ArrayList<>();
Map<String, String> userMsg = new HashMap<>();
userMsg.put("role", "user");
userMsg.put("content", message);
messages.add(userMsg);
requestBody.put("messages", messages);
requestBody.put("model", "deepseek-ai/DeepSeek-R1");
requestBody.put("stream", true);
requestBody.put("temperature", 0.5);
requestBody.put("max_new_tokens", 4000);
requestBody.put("conv_uid", UUID.randomUUID().toString());
String json = mapper.writeValueAsString(requestBody);
// 構(gòu)建請求
okhttp3.Request request = new okhttp3.Request.Builder()
.url("http://localhost:5670/api/v2/chat/completions")
.post(okhttp3.RequestBody.create(json,
okhttp3.MediaType.parse("application/json; charset=utf-8")))
.addHeader("accept", "application/json")
.addHeader("Content-Type", "application/json")
.build();
// 執(zhí)行請求并處理流式響應(yīng)
try (okhttp3.Response apiResponse = client.newCall(request).execute()) {
if (apiResponse.isSuccessful()) {
ResponseBody body = apiResponse.body();
if (body != null) {
// 逐行讀取流式響應(yīng)
BufferedReader reader = new BufferedReader(
new InputStreamReader(body.byteStream())
);
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
// 直接轉(zhuǎn)發(fā)給前端
writer.write(line + "\n\n");
writer.flush();
}
}
}
}
}
} catch (Exception e) {
log.error("流式請求異常", e);
writer.write("data: {\"error\": \"服務(wù)異常: " + e.getMessage() + "\"}\n\n");
writer.flush();
}
}
6. 前端調(diào)用(極簡版)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>流式問答</title>
</head>
<body>
<input type="text" id="question" placeholder="輸入問題">
<button onclick="ask()">提問</button>
<div id="answer" style="white-space: pre-wrap; border:1px solid #ccc; padding:10px; margin-top:10px;"></div>
<script>
function ask() {
const question = document.getElementById('question').value;
const answerDiv = document.getElementById('answer');
answerDiv.innerHTML = '';
// 使用EventSource接收流式數(shù)據(jù)
const es = new EventSource(`/api/question/answer/stream?message=${encodeURIComponent(question)}`);
es.onmessage = function(e) {
const data = e.data;
if (data === '[DONE]') {
es.close();
return;
}
try {
const json = JSON.parse(data);
if (json.choices && json.choices[0].delta && json.choices[0].delta.content) {
answerDiv.innerHTML += json.choices[0].delta.content;
}
} catch(err) {
// 忽略解析錯(cuò)誤
}
};
es.onerror = function() {
es.close();
};
}
</script>
</body>
</html>
到此這篇關(guān)于java調(diào)用第三方接口實(shí)現(xiàn)流式輸出的示例代碼的文章就介紹到這了,更多相關(guān)java 流式輸出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)Excel批量導(dǎo)入數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)Excel批量導(dǎo)入數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08
基于JAVA中Jersey處理Http協(xié)議中的Multipart的詳解
之前在基于C#開發(fā)彩信用最原始的StringBuilder拼接字符串方式處理過Multipart?,F(xiàn)在在做一個(gè)項(xiàng)目的時(shí)候,由于之前的技術(shù)路線都是使用Jersey處理Http這塊,為了保持技術(shù)路線一致,研究了一下如何使用Jersey處理Http協(xié)議中的Multipart2013-05-05
使用IDEA異常斷點(diǎn)來定位java.lang.ArrayStoreException的問題
這篇文章主要介紹了使用IDEA異常斷點(diǎn)來定位java.lang.ArrayStoreException的問題,平常開發(fā)過程中面對這種描述不夠清楚,無法定位具體原因的問題該如何處理,下面我們來一起學(xué)習(xí)一下吧2019-06-06
Java中ArrayList具體實(shí)現(xiàn)之簡單的洗牌算法
這篇文章主要給大家介紹了Java中ArrayList具體實(shí)現(xiàn)之簡單的洗牌算法,文中通過代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-12-12
SpringBoot項(xiàng)目Web攔截器使用的多種方式
在Spring?Boot應(yīng)用中,Web攔截器(Interceptor)是一種用于在請求處理的不同階段執(zhí)行自定義邏輯的機(jī)制,下面給大家介紹SpringBoot項(xiàng)目Web攔截器使用的多種方式,感興趣的朋友一起看看吧2025-05-05

