一、项目用到的大模型------月之暗面简介
月之暗面(Moonshot AI),它所提供的moonshot-v1-8k-vision-preview
模型,支持图像与文本的联合分析,我们可以通过API将图片和文本同时输入AI模型,获取智能分析结果。
该模型的特点如下:
- 多模态输入:支持同时处理图片和文本
- 高精度视觉理解:可识别图片中的物体、场景、文字等信息
- 自然语言输出:以中文或英文返回分析结果
本项目通过调用月之暗面的API,构建一个具备图片上传、预览和AI分析功能的前端页面,演示如何将AI能力无缝集成到Web应用中。
二、开始前的准备
由于代码需要调用月之暗面大模型的接口,为此,我们需要先做好以下准备:
-
打开月之暗面官网
-
点击右上角
用户中心
进行登录

3.在左侧找到API Key 管理
,随后新建API Key
,创建后记得复制你的密钥。

- 在项目根目录下创建
.env.local
文件,随后按下面格式输入你的密钥: VITE_API_KEY= 你的密钥
注意事项:
VITE_
前缀是Vite框架的约定,确保环境变量在客户端可访问。- 生产环境建议通过后端中转API密钥,避免直接暴露。
三、服务代理配置
为解决跨域问题,我们使用Vite的代理功能,在vite.config.js
进行以下配置:
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'https://api.moonshot.cn',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '/v1')
}
}
}
})
作用:
- 将本地请求
/api/chat/completions
映射到https://api.moonshot.cn/v1/chat/completions
, 避免浏览器的同源策略限制。
四、完整代码和效果展示
1. 效果展示
为了使屏幕前的你对接下来实现的功能有更明确的认识,我将先进行页面效果展示:

效果说明:在该页面中,用户可以通过上传一张本地的照图片,在点击提交后,ai便会生成一段描述该图片的文字。
2. 完整代码
App.jsx
import { useState } from 'react'
import './App.css'
function App() {
const [content, setContent] = useState('')
const [imgBase64Data, setImgBase64Data] = useState('')
const [isValid, setIsValid] = useState(false)
const updateBase64Data = (e) => {
const file = e.target.files[0]
if (!file) return;
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
setImgBase64Data(reader.result)
setIsValid(true)
}
}
const update = async() => {
if(!imgBase64Data) return;
const endpoint = 'https://api.moonshot.cn/v1/chat/completions';
const headers ={
'Content-Type':'application/json',
'Authorization':`Bearer ${import.meta.env.VITE_API_KEY}`
}
setContent('正在加载中...')
const response = await fetch(endpoint,{
method:'POST',
headers,
body:JSON.stringify({
model:'moonshot-v1-8k-vision-preview',
messages:[
{
role:'user',
content:[
{
type:'image_url',
image_url:{
"url":imgBase64Data
}
},
{
type:'text',
text:'请用中文描述图片'
}
]
}
],
})
})
const data = await response.json()
setContent(data.choices[0].message.content)
}
return (
<div className="container">
<div className="output">
<div className="preview">
{
imgBase64Data && <img src={imgBase64Data} alt="" />
}
</div>
</div>
<div>
<label htmlFor='fileInput'>文件:</label>
<input
type="file"
id='fileInput'
className='input'
accept='.jpg,.png,.gif,.jpeg'
onChange={updateBase64Data}
/>
<button onClick={update} disabled={!isValid}>提交</button>
</div>
{content}
</div>
)
}
export default App
五、代码分析
5.1 导入模块和组件定义
jsx
import { useState } from 'react'
import './App.css'
useState
- React内置的Hook函数,用于管理响应式状态数据
- 作用 :
- 创建可变状态变量(
content
,imgBase64Data
,isValid
) - 当状态更新时,自动触发组件重渲染
- 创建可变状态变量(
5.2 状态变量初始化
jsx
const [content, setContent] = useState('')
const [imgBase64Data, setImgBase64Data] = useState('')
const [isValid, setIsValid] = useState(false)
5.2.1 状态变量说明
变量名 | 类型 | 作用 |
---|---|---|
content |
string | 存储AI分析结果 |
imgBase64Data |
string | 存储图片的Base64编码 |
isValid |
boolean | 控制表单验证状态 |
5.2.2 技术点
- 响应式更新:状态更新会触发组件重渲染
- 初始值设定:空字符串表示未选择文件或未生成结果
- 状态组合:三个状态共同管理应用的交互流程
5.3 图片处理函数 updateBase64Data
jsx
const updateBase64Data = (e) => {
const file = e.target.files[0]
console.log(file)
if (!file) return;
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
setImgBase64Data(reader.result)
setIsValid(true)
}
}
5.3.1 文件操作流程
-
获取文件对象:
e.target.files[0]
获取用户选择的第一个文件File API
提供对本地文件的读取能力
-
Base64编码转换:
FileReader
异步读取文件内容readAsDataURL()
将文件转为data:image/png;base64,iVBORw0KG...
格式
-
状态更新:
- 通过
onload
回调处理异步结果 - 更新
imgBase64Data
和表单验证状态
- 通过
5.3.2 技术点详解
- HTML5 File API :
- 允许JavaScript访问本地文件系统
- 支持文件类型检测(
file.type
)、大小读取等
- Base64编码 :
- Data URLs格式:
data:<mediatype>;<encoding>,<data>
- 优点:直接嵌入图片数据,避免额外请求
- 缺点:体积比原始文件大33%
- Data URLs格式:
5.4 AI分析函数 update
jsx
const update = async () => {
if (!imgBase64Data) return;
const endpoint = 'https://api.moonshot.cn/v1/chat/completions';
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${import.meta.env.VITE_API_KEY}`
}
setContent('正在加载中...')
const response = await fetch(endpoint, {
method: 'POST',
headers,
body: JSON.stringify({
model: 'moonshot-v1-8k-vision-preview',
messages: [
{
role: 'user',
content: [
{ type: 'image_url', image_url: { "url": imgBase64Data } },
{ type: 'text', text: '请用中文描述图片' }
]
}
],
})
})
const data = await response.json()
setContent(data.choices[0].message.content)
}
5.4.1 API请求流程
-
请求准备:
- 构造API地址和请求头
- 设置Bearer Token认证(通过环境变量注入)
-
请求体构建:
- 使用
moonshot-v1-8k-vision-preview
模型 - 构建多模态输入内容(图片+文本指令)
- 使用
-
异步处理:
- 使用
async/await
同步化异步操作 - 通过
fetch
发起HTTP POST请求
- 使用
-
结果处理:
- 解析JSON响应
- 更新分析结果状态
5.4.2 技术点
- HTTP请求头 :
Content-Type
声明请求体格式Authorization
Bearer Token认证
- 多模态输入 :
- 支持同时处理图像和文本
- 消息结构符合OpenAI兼容格式
- 错误处理缺失 :
- 当前代码缺少
try/catch
异常捕获 - 需补充错误提示逻辑(参考知识库中的错误示例)
- 当前代码缺少
5.5 UI渲染部分
jsx
return (
<div className="container">
<div className="output">
<div className="preview">
{
imgBase64Data && <img src={imgBase64Data} alt="" />
}
</div>
</div>
<div>
<label htmlFor='fileInput'>文件:</label>
<input
type="file"
id='fileInput'
className='input'
accept='.jpg,.png,.gif,.jpeg'
onChange={updateBase64Data}
/>
<button onClick={update} disabled={!isValid}>提交</button>
</div>
{content}
</div>
)
5.5.1 组件结构分析
-
容器布局:
- 使用
container
类控制整体布局 - 包含预览区、操作区和结果展示区
- 使用
-
条件渲染:
imgBase64Data && <img ... />
实现图片存在时才渲染- 避免未选择文件时渲染空img标签
-
表单交互:
accept
属性限制可选文件类型disabled={!isValid}
动态控制按钮可用状态
5.5.2 技术点
- JSX语法 :
- 使用
&&
操作符实现条件渲染 - 表单元素通过
onChange
绑定处理函数
- 使用
- 无障碍设计 :
htmlFor
与id
关联提升可访问性- 输入框添加
accept
属性限制文件类型
六、代码执行流程图
scss
用户选择图片
↓
FileReader读取为Base64
↓
更新imgBase64Data和isValid状态
↓
点击提交按钮(disabled状态解除)
↓
构造API请求体
↓
发送POST请求到Moonshot API
↓
接收JSON响应
↓
解析并更新content状态
↓
页面重新渲染显示分析结果
七、总结
通过本文的实践,我们完成了以下目标:
- 集成月之暗面API:将AI视觉分析能力嵌入前端页面
- 实现核心功能 :
- 图片上传与Base64编码转换
- 动态预览与表单验证
- 异步请求与结果展示
- 掌握关键技术点 :
- React状态管理(
useState
) - 文件操作与Base64编码
- 原生JavaScript异步编程
- Vite环境变量与代理配置
- React状态管理(