React智能前端:从零开始写的图片分析页面实战

一、项目用到的大模型------月之暗面简介

月之暗面(Moonshot AI),它所提供的moonshot-v1-8k-vision-preview模型,支持图像与文本的联合分析,我们可以通过API将图片和文本同时输入AI模型,获取智能分析结果。

该模型的特点如下:

  • 多模态输入:支持同时处理图片和文本
  • 高精度视觉理解:可识别图片中的物体、场景、文字等信息
  • 自然语言输出:以中文或英文返回分析结果

本项目通过调用月之暗面的API,构建一个具备图片上传、预览和AI分析功能的前端页面,演示如何将AI能力无缝集成到Web应用中。


二、开始前的准备

由于代码需要调用月之暗面大模型的接口,为此,我们需要先做好以下准备:

  1. 打开月之暗面官网

  2. 点击右上角用户中心进行登录

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

  1. 在项目根目录下创建.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 文件操作流程

  1. 获取文件对象

    • e.target.files[0] 获取用户选择的第一个文件
    • File API 提供对本地文件的读取能力
  2. Base64编码转换

    • FileReader 异步读取文件内容
    • readAsDataURL() 将文件转为data:image/png;base64,iVBORw0KG...格式
  3. 状态更新

    • 通过onload回调处理异步结果
    • 更新imgBase64Data和表单验证状态

5.3.2 技术点详解

  • HTML5 File API
    • 允许JavaScript访问本地文件系统
    • 支持文件类型检测(file.type)、大小读取等
  • Base64编码
    • Data URLs格式:data:<mediatype>;<encoding>,<data>
    • 优点:直接嵌入图片数据,避免额外请求
    • 缺点:体积比原始文件大33%

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请求流程

  1. 请求准备

    • 构造API地址和请求头
    • 设置Bearer Token认证(通过环境变量注入)
  2. 请求体构建

    • 使用moonshot-v1-8k-vision-preview模型
    • 构建多模态输入内容(图片+文本指令)
  3. 异步处理

    • 使用async/await同步化异步操作
    • 通过fetch发起HTTP POST请求
  4. 结果处理

    • 解析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绑定处理函数
  • 无障碍设计
    • htmlForid 关联提升可访问性
    • 输入框添加accept属性限制文件类型


六、代码执行流程图

scss 复制代码
用户选择图片
   ↓
FileReader读取为Base64
   ↓
更新imgBase64Data和isValid状态
   ↓
点击提交按钮(disabled状态解除)
   ↓
构造API请求体
   ↓
发送POST请求到Moonshot API
   ↓
接收JSON响应
   ↓
解析并更新content状态
   ↓
页面重新渲染显示分析结果

七、总结

通过本文的实践,我们完成了以下目标:

  1. 集成月之暗面API:将AI视觉分析能力嵌入前端页面
  2. 实现核心功能
    • 图片上传与Base64编码转换
    • 动态预览与表单验证
    • 异步请求与结果展示
  3. 掌握关键技术点
    • React状态管理(useState
    • 文件操作与Base64编码
    • 原生JavaScript异步编程
    • Vite环境变量与代理配置
相关推荐
canonical_entropy3 分钟前
Nop Chaos Flux:百度AMIS之后的下一代低代码渲染引擎
前端·低代码·ai编程
时光足迹21 分钟前
Tiptap 简单编辑器模版
前端·javascript·react.js
向量引擎31 分钟前
为什么大厂做 RAG,都要加一层向量引擎中转站?
人工智能·gpt·aigc·api·key
JSLove35 分钟前
nginx入门
前端·nginx
时光足迹36 分钟前
ThreeJS之GUI控制器
前端·javascript·three.js
时光足迹36 分钟前
Tiptap编辑器
前端·javascript·react.js
时光足迹40 分钟前
电子书阅读器之笔记高亮(跨段处理)
前端·javascript·react.js
Dabei43 分钟前
Android 副屏(Virtual Display)创建与悬浮窗画中画显示实战
前端·架构
Hello-Mr.Wang1 小时前
【保姆级教程】MasterGo MCP + Cursor 一键实现 UI 设计稿还原
前端·javascript·vue.js·ai编程