node.js + html调用ChatGPTApi实现Ai网站demo(带源码)
文章目录
- 前言
- 一、demo演示
- 二、node.js 使用步骤
- 1.引入库
- 2.引入包
- 前端HTML调用接口和UI
- 所有文件
- 总结
前言
关注博主,学习每天一个小demo 今天是Ai对话网站
又到了每天一个小demo的时候咯,前面我写了多人实时对话demo、和视频转换demo,今天我来使用 node.js + html 调用chatGpt Api实现一个Ai 流式对话小demo,当然现在主流的各种APi调用方式一致,你也可以接入deepseek,或者第三方的接口,效果一样
一、demo演示
下面是一个简单的demo演示,并且是支持流式的

二、node.js 使用步骤
1.引入库
代码如下:
先初始化一个package.js 文件。
npm init -y
我们需要安装 express、cors、openAi、dotenv三方库
{"name": "web","version": "1.0.0","main": "server.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","start": "node server.js"},"keywords": [],"author": "","license": "ISC","description": "","dependencies": {"axios": "^1.7.9","cors": "^2.8.5","dotenv": "^16.0.3","express": "^4.18.2","openai": "^4.0.0"}
}
2.引入包
创建server.js 引入我们的第三方包 编写接口逻辑
**特别关注:
- baseURL: 这里大家可以替换其他的APi 比如deepseek(好像目前不能充值了),或者一些第三方的API地址。
- apiKey: 这里大家把key可以直接填写上,我这里为了学习知识,新建了一个.env 文件。**
const express = require('express');
const cors = require('cors');
const OpenAI = require('openai');
require('dotenv').config();const app = express();
app.use(express.json());
app.use(cors());// 初始化OpenAI客户端
const openai = new OpenAI({apiKey: process.env.DEEPSEEK_API_KEY, baseURL: "https://api.openai.com/v1" // 使用OpenAI官方API地址 这里可以可以使用别的Api地址// 比如 deepseek 或者其他的第三方的一些// baseURL: "https://api.deepseek.com/v1"
});let conversationHistory = [];app.post('/chat', async (req, res) => {try {const { message } = req.body;// 设置响应头,支持流式输出res.setHeader('Content-Type', 'text/event-stream');res.setHeader('Cache-Control', 'no-cache');res.setHeader('Connection', 'keep-alive');res.setHeader('Access-Control-Allow-Origin', '*'); // 添加CORS支持// 添加用户消息到历史记录conversationHistory.push({ role: "user", content: message });const stream = await openai.chat.completions.create({model: "gpt-3.5-turbo",messages: [{ role: "system", content: "You are a helpful assistant." },...conversationHistory],temperature: 0.7,max_tokens: 1000,stream: true,});let fullResponse = '';// 确保每次写入后立即刷新缓冲区for await (const chunk of stream) {const content = chunk.choices[0]?.delta?.content || '';if (content) {fullResponse += content;const dataToSend = JSON.stringify({ content });console.log('Sending to client:', dataToSend);res.write(`data: ${dataToSend}\n\n`);// 强制刷新缓冲区if (res.flush) {res.flush();}}}// 将完整回复添加到对话历史conversationHistory.push({ role: "assistant", content: fullResponse });if (conversationHistory.length > 10) {conversationHistory = conversationHistory.slice(-10);}res.write('data: [DONE]\n\n');if (res.flush) {res.flush();}res.end();} catch (error) {console.error('Error:', error);res.write(`data: ${JSON.stringify({ error: '服务器错误' })}\n\n`);res.end();}
});const PORT = 3000;
app.listen(PORT, () => {console.log(`服务器运行在端口 ${PORT}`);
});
在根目录执行 node server.js 启动服务
前端HTML调用接口和UI
不墨迹哈,直接把所有代码贴过来 大家好直接研究代码,我就不用一行一行解读了,没啥东西,难处就是对流式数据的一个处理
特别注意, 不要直接点击html打开页面,在vscode里面安装扩展Live Server,然后点击右下角 Go live启动一个微服务。


<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ChatGPT 聊天</title><style>#chat-container {width: 80%;max-width: 800px;margin: 20px auto;padding: 20px;border: 1px solid #ccc;border-radius: 5px;}#chat-messages {height: 400px;overflow-y: auto;margin-bottom: 20px;padding: 10px;border: 1px solid #eee;}.message {margin: 10px 0;padding: 10px;border-radius: 5px;}.user-message {background-color: #e3f2fd;margin-left: 20%;}.bot-message {background-color: #f5f5f5;margin-right: 20%;}#message-form {display: flex;gap: 10px;}#message-input {flex: 1;padding: 8px;}.typing {opacity: 0.5;}.cursor {display: inline-block;width: 2px;height: 15px;background: #000;margin-left: 2px;animation: blink 1s infinite;}@keyframes blink {0%, 100% { opacity: 1; }50% { opacity: 0; }}</style>
</head>
<body><div id="chat-container"><div id="chat-messages"></div><form id="message-form"><input type="text" id="message-input" placeholder="输入消息..." required><button type="submit" id="submit-btn">发送</button></form></div><script>const messageForm = document.getElementById('message-form');const messageInput = document.getElementById('message-input');const chatMessages = document.getElementById('chat-messages');const submitBtn = document.getElementById('submit-btn');messageForm.addEventListener('submit', async (e) => {e.preventDefault();const message = messageInput.value.trim();if (!message) return;// 禁用输入和发送按钮messageInput.disabled = true;submitBtn.disabled = true;// 显示用户消息addMessage(message, 'user');messageInput.value = '';// 创建机器人回复的消息框const botMessageDiv = document.createElement('div');botMessageDiv.className = 'message bot-message typing';chatMessages.appendChild(botMessageDiv);// 添加光标const cursor = document.createElement('span');cursor.className = 'cursor';botMessageDiv.appendChild(cursor);try {const response = await fetch('http://localhost:3000/chat', {method: 'POST',headers: {'Content-Type': 'application/json','Accept': 'text/event-stream'},body: JSON.stringify({ message })});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const reader = response.body.getReader();const decoder = new TextDecoder();let botResponse = '';let buffer = '';try {while (true) {const { value, done } = await reader.read();// 如果流结束了,就退出循环if (done) {console.log('Stream complete');break;}// 确保有值才处理if (value) {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: ')) continue;const data = line.slice(6).trim();if (data === '[DONE]') {botMessageDiv.classList.remove('typing');cursor.remove();continue;}try {const parsedData = JSON.parse(data);if (parsedData.content) {botResponse += parsedData.content;botMessageDiv.textContent = botResponse;botMessageDiv.appendChild(cursor);chatMessages.scrollTop = chatMessages.scrollHeight;}} catch (e) {console.error('JSON解析错误:', e, 'Raw data:', data);}}}}// 处理最后可能残留的数据if (buffer.trim()) {const finalText = decoder.decode(); // 完成流的解码if (finalText) {buffer += finalText;const lines = buffer.split('\n');for (const line of lines) {if (line.trim() === '' || !line.startsWith('data: ')) continue;const data = line.slice(6).trim();if (data === '[DONE]') continue;try {const parsedData = JSON.parse(data);if (parsedData.content) {botResponse += parsedData.content;botMessageDiv.textContent = botResponse;}} catch (e) {console.error('最终解析错误:', e, 'Raw data:', data);}}}}} catch (streamError) {console.error('Stream processing error:', streamError);throw streamError;}} catch (error) {console.error('Error:', error);botMessageDiv.textContent = '抱歉,发生错误。';botMessageDiv.classList.remove('typing');cursor.remove();} finally {messageInput.disabled = false;submitBtn.disabled = false;messageInput.focus();}});function addMessage(text, sender) {const messageDiv = document.createElement('div');messageDiv.className = `message ${sender}-message`;messageDiv.textContent = text;chatMessages.appendChild(messageDiv);chatMessages.scrollTop = chatMessages.scrollHeight;}</script>
</body>
</html>
所有文件
- .env 存储了APi-key
- index.html 前端代码
- server.js 后段代码


总结
尽可能的多学习一些知识,或许以后用不到,关注我每天练习一个小demo。
相关文章:
node.js + html调用ChatGPTApi实现Ai网站demo(带源码)
文章目录 前言一、demo演示二、node.js 使用步骤1.引入库2.引入包 前端HTML调用接口和UI所有文件总结 前言 关注博主,学习每天一个小demo 今天是Ai对话网站 又到了每天一个小demo的时候咯,前面我写了多人实时对话demo、和视频转换demo,今天…...
sql语言语法的学习
sql通用语法 sql分类 DDL(操作数据库和表) 操作数据库 操作表_查询 操作表_创建 举例: 操作表_删除 操作表_修改 DML(增删改表中数据) DML添加数据 DML删除数据 DML修改数据 DQL 单表查询 基础查询 条件查询 案例演示: 排序查询 聚合函数 分组查询…...
力扣 最长递增子序列
动态规划,二分查找。 题目 由题,从数组中找一个最长子序列,不难想到,当这个子序列递增子序列的数越接近时是越容易拉长的。从dp上看,当遍历到这个数,会从前面的dp选一个最大的数加上当前数,注意…...
【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用
【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用 【承接商业广告,如需商业合作请+v17740568442】 文章目录 【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用个人配置详情一、安装ollama二、下载deepseek版本…...
visutal studio 2022使用qcustomplot基础教程
编译 下载,2.1.1版支持到Qt6.4 。 拷贝qcustomplot.h和qcustomplot.cpp到项目源目录(Qt project)。 在msvc中将它俩加入项目中。 使用Qt6.8,需要修改两处代码: L6779 # if QT_VERSION > QT_VERSION_CHECK(5, 2, …...
Linux:线程概念、理解、控制
目录 一、认识线程 1.认识线程V1 2.认识线程V2 3.认识线程V3 4.认识线程V4 5.认识线程V5 二、线程控制 1.前言 2.创建线程 3.线程等待 4.线程终止 5.线程分离 三、线程理解 一、认识线程 1.认识线程V1 借用大多数计算机教材的话,线程是进程的一个执行…...
Postman如何流畅使用DeepSeek
上次写了一篇文章是用chatBox调用api的方式使用DeepSeek,但是实际只能请求少数几次就不再能给回响应。这回我干脆用最原生的方法Postman调用接口请求好了。 1. 通过下载安装Postman软件 postman下载(https://pan.quark.cn/s/c8d1c7d526f3),包含7.0和10…...
K8S下载离线安装包所需文件
下载相关文件 官网下载地址集合https://kubernetes.io/zh-cn/releases/download/ 下载相关镜像 官网镜像描述 所有 Kubernetes 容器镜像都被部署到 registry.k8s.io 容器镜像仓库。 容器镜像支持架构registry.k8s.io/kube-apiserver:v1.32.0amd64, arm, arm64, ppc64le, …...
探索Hugging Face:开源AI社区的核心工具与应用实践
引言:AI民主化的先锋 在自然语言处理(NLP)领域,Hugging Face已成为开源社区的代名词。这个成立于2016年的平台,通过提供易用的工具和丰富的预训练模型库,彻底改变了开发者使用和部署AI模型的方式。截至202…...
【操作系统】深入理解Linux物理内存
物理内存的组织结构 我们平时所称的内存也叫随机访问存储器也叫 RAM 。RAM 分为两类: 一类是静态 RAM( SRAM ),这类 SRAM 用于 CPU 高速缓存 L1Cache,L2Cache,L3Cache。其特点是访问速度快,访…...
npm 私服使用介绍
一、导读 本文主要介绍 npm 私服的使用,至于 npm 私服搭建的过程,可以看本人之前的文章《Docker 部署 verdaccio 搭建 npm 私服》 二、前置条件 npm私服地址:http://xxx.xxx.xxx.xxx:port/ 三、本地 npm 源切换 使用nrm,可以方…...
安全筑基,智能赋能:BeeWorks IM引领企业协同新纪元
在数字经济高速发展的今天,企业通讯系统已从单纯的信息传递工具演变为支撑业务创新的核心平台。传统通讯工具在安全性、智能化、协同性等方面的不足,严重制约着企业的数字化转型进程。BeeWorks IM系统以其创新的技术架构和智能化功能,正在重新…...
水务+AI应用探索(一)| FastGPT+DeepSeek 本地部署
在当下的科技浪潮中,AI 无疑是最炙手可热的焦点之一,其强大的能力催生出了丰富多样的应用场景,广泛渗透到各个行业领域。对于水务行业而言,AI 的潜力同样不可估量。为了深入探究 AI 在水务领域的实际应用成效,切实掌握…...
[JVM篇]垃圾回收器
垃圾回收器 Serial Seral Old PartNew CMS(Concurrent Mark Sweep) Parallel Scavenge Parallel Old G1 ZGC...
SQL Server:查看当前连接数和最大连接数
目录标题 **1. 查看当前连接数****使用系统视图****使用动态管理视图** **2. 查看最大连接数****通过配置选项****通过服务器属性** **3. 查看连接数的实时变化****4. 设置最大连接数****5. 查看连接的详细信息****6. 使用 SQL Server Management Studio (SSMS)****7. 使用 SQL…...
DeepSeek应用——与PyCharm的配套使用
目录 一、配置方法 二、使用方法 三、注意事项 1、插件市场无continue插件 2、无结果返回,且在本地模型报错 记录自己学习应用DeepSeek的过程,使用的是自己电脑本地部署的私有化蒸馏模型...... (举一反三,这个不单单是可以用…...
【第15章:量子深度学习与未来趋势—15.3 量子深度学习在图像处理、自然语言处理等领域的应用潜力分析】
一、开篇:为什么我们需要关注这场"量子+AI"的世纪联姻? 各位技术爱好者们,今天我们要聊的这个话题,可能是未来十年最值得押注的技术革命——量子深度学习。这不是简单的"1+1=2"的物理叠加,而是一场可能彻底改写AI发展轨迹的范式转移。 想象这样一个…...
多模态基础模型训练笔记-第一篇InternVL-g
一、TL;DR 将之前所有训练过的大模型的过程都总结和回忆一下,遇到的坑别忘了 二、问题记录 还是注意镜像的选择,选择社区最火的镜像,然后下载好对应的数据,主要显卡的选择,这个时候4090已经带不动了&…...
MyBatis:动态SQL高级标签使用方法指南
一、引言 目前互联网大厂在搭建后端Java服务时,常使用Springboot搭配Mybatis/Mybatis-plus的框架。Mybatis/Mybatis-plus之所以能成为当前国内主流的持久层框架,与其本身的优点有关:支持定制动态 SQL、存储过程及高级映射,简化数…...
使用grafana v11 建立k线(蜡烛图)仪表板
先看实现的结果 沪铜主力合约 2025-02-12 的1分钟k线图 功能介绍: 左上角支持切换主力合约,日期,实现动态加载数据. 项目背景: 我想通过前端展示期货指定品种某1天的1分钟k线,类似tqsdk 的web_gui 生成图形化界面— TianQin Python SDK 3.7.8 文档 项目架构: 后端: fastap…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
