Wan2.1-UMT5快速上手:Node.js环境下的API服务封装与调用
你是不是遇到过这种情况?团队里部署了一个强大的AI模型,比如Wan2.1-UMT5,它功能很酷,但调用方式对前端同学来说有点“黑盒”,或者直接调用后端服务的API存在安全、管理上的麻烦。
这时候,一个用Node.js搭建的中间层API服务就成了完美的解决方案。它就像一座桥,前端用熟悉的HTTP请求就能访问,后端则负责与复杂的GPU平台API打交道。今天,我就带你从零开始,手把手搭建这样一个服务,让你快速把Wan2.1-UMT5的能力集成到自己的应用里。
1. 项目初始化与环境准备
万事开头先搭台。我们先来把Node.js环境准备好,并创建一个干净的Express项目。
1.1 Node.js安装与检查
如果你还没安装Node.js,可以去官网下载最新的LTS(长期支持)版本。安装完成后,打开终端(或命令行),用下面两行命令验证一下:
node --version npm --version如果能看到版本号(比如v18.x.x和9.x.x),说明安装成功。我建议使用Node.js 16或以上版本,能更好地兼容一些现代包。
1.2 创建项目并安装核心依赖
找个你喜欢的目录,新建一个项目文件夹,比如叫wan2-umt5-api-proxy,然后进去初始化项目并安装我们需要的包。
mkdir wan2-umt5-api-proxy cd wan2-umt5-api-proxy npm init -y接下来,安装几个核心的依赖包:
npm install express axios dotenv multer简单说一下这几个包是干嘛的:
- express:Node.js里最流行的Web框架,用来快速搭建我们的API服务器。
- axios:一个非常好用的HTTP客户端,用来向星图GPU平台的API发送请求,比原生的
fetch或http模块更友好。 - dotenv:管理环境变量的神器。我们把API密钥、服务地址这些敏感信息放在
.env文件里,用这个包来读取,代码里就不会暴露关键信息了。 - multer:处理
multipart/form-data格式数据的中间件,主要用来接收前端上传的文件。
对于开发阶段,我们还需要一个工具来自动重启服务,这样改代码就不用每次都手动停止了。
npm install --save-dev nodemon安装完成后,打开package.json文件,在scripts部分添加一个启动命令:
{ "scripts": { "start": "node app.js", "dev": "nodemon app.js" } }以后开发时,就用npm run dev启动,每次保存文件,服务都会自动重启。
2. 构建基础Express服务与应用配置
环境好了,我们来敲代码,先把服务的骨架搭起来。
2.1 创建主应用文件
在项目根目录下,创建一个app.js文件,这是我们的入口文件。
// app.js const express = require('express'); const dotenv = require('dotenv'); // 加载.env文件中的环境变量 dotenv.config(); const app = express(); const PORT = process.env.PORT || 3000; // 中间件:解析JSON格式的请求体 app.use(express.json()); // 中间件:解析URL-encoded格式的请求体 app.use(express.urlencoded({ extended: true })); // 一个简单的根路由,用于测试服务是否运行 app.get('/', (req, res) => { res.json({ message: 'Wan2.1-UMT5 API 代理服务运行正常!' }); }); // 启动服务器 app.listen(PORT, () => { console.log(`🚀 代理服务已启动,监听端口:${PORT}`); console.log(`📡 本地访问地址:http://localhost:${PORT}`); });现在,在终端运行npm run dev,你应该能看到成功启动的日志。打开浏览器访问http://localhost:3000,就能看到我们返回的JSON消息了。
2.2 配置环境变量
在根目录下创建一个名为.env的文件。这里我们会存放所有不想写死在代码里的配置。
# .env PORT=3000 # 星图GPU平台API的基础地址(请替换为你的实际地址) GPU_API_BASE_URL=https://your-gpu-platform.com/api/v1 # 你在星图平台获取的API密钥(非常重要,请妥善保管) GPU_API_KEY=your_super_secret_api_key_here # 文件上传的临时存储目录 UPLOAD_DIR=./uploads重要提示:.env文件必须添加到.gitignore中,避免将密钥等敏感信息提交到代码仓库。GPU_API_BASE_URL和GPU_API_KEY需要你根据星图GPU平台提供的实际信息进行填写。
3. 封装GPU平台API调用
这是核心部分,我们要创建一个专门的模块,来封装所有与Wan2.1-UMT5模型交互的细节。
3.1 创建API客户端模块
在项目根目录下创建一个services文件夹,然后在里面新建一个gpuApiClient.js文件。
// services/gpuApiClient.js const axios = require('axios'); // 从环境变量读取配置 const GPU_API_BASE_URL = process.env.GPU_API_BASE_URL; const GPU_API_KEY = process.env.GPU_API_KEY; // 创建配置好的axios实例 const apiClient = axios.create({ baseURL: GPU_API_BASE_URL, timeout: 30000, // 30秒超时,对于AI模型推理可能较长,可根据需要调整 headers: { 'Authorization': `Bearer ${GPU_API_KEY}`, 'Content-Type': 'application/json', } }); /** * 封装调用Wan2.1-UMT5文本生成模型的函数 * @param {Object} payload - 发送给模型的参数,具体结构需参考模型API文档 * @returns {Promise<Object>} - 模型返回的结果 */ async function generateText(payload) { try { // 假设模型文本生成的端点路径是 `/generate/text` const response = await apiClient.post('/generate/text', payload); return response.data; } catch (error) { console.error('调用文本生成API失败:', error.response?.data || error.message); // 将底层API的错误信息向上抛出,便于路由层处理 throw new Error(`文本生成请求失败: ${error.message}`); } } /** * 封装调用Wan2.1-UMT5文件处理(如图片理解、视频生成)模型的函数 * 此函数处理需要上传文件的场景 * @param {String} filePath - 本地临时文件的路径 * @param {Object} additionalParams - 其他文本参数 * @returns {Promise<Object>} - 模型返回的结果 */ async function processWithFile(filePath, additionalParams = {}) { try { // 需要使用FormData格式上传,这里我们用'form-data'包,需要额外安装 // npm install form-data const FormData = require('form-data'); const fs = require('fs'); const formData = new FormData(); // 创建文件流并附加到formData中,字段名'file'需根据API文档确定 formData.append('file', fs.createReadStream(filePath)); // 附加其他文本参数 Object.keys(additionalParams).forEach(key => { formData.append(key, additionalParams[key]); }); // 注意:发送FormData时,headers应由form-data库自动生成 const response = await apiClient.post('/process/file', formData, { headers: formData.getHeaders() // 获取正确的Content-Type(multipart/form-data)和边界 }); return response.data; } catch (error) { console.error('调用文件处理API失败:', error.response?.data || error.message); throw new Error(`文件处理请求失败: ${error.message}`); } } module.exports = { generateText, processWithFile };这个模块做了几件事:
- 集中管理了API地址和密钥。
- 使用
axios.create创建了一个预配置的客户端,避免重复代码。 - 提供了两个函数:一个处理纯文本请求,一个处理需要上传文件的请求。
- 对错误进行了初步捕获和包装,让调用方更容易处理。
记得运行npm install form-data来安装文件上传需要的这个包。
4. 实现业务路由与接口
现在我们来创建具体的API路由,暴露给前端调用。
4.1 创建路由文件
在根目录下创建routes文件夹,然后新建apiRoutes.js。
// routes/apiRoutes.js const express = require('express'); const router = express.Router(); const multer = require('multer'); const path = require('path'); const fs = require('fs').promises; const { generateText, processWithFile } = require('../services/gpuApiClient'); // 确保上传目录存在 const uploadDir = process.env.UPLOAD_DIR || './uploads'; const fsSync = require('fs'); if (!fsSync.existsSync(uploadDir)) { fsSync.mkdirSync(uploadDir, { recursive: true }); } // 配置multer:存储上传的文件到指定目录,并保留原始文件名 const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, uploadDir); }, filename: function (req, file, cb) { // 使用时间戳+原文件名,避免重名 const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, uniqueSuffix + path.extname(file.originalname)); } }); const upload = multer({ storage: storage }); /** * 文本生成接口 * POST /api/generate/text */ router.post('/generate/text', async (req, res) => { try { console.log('收到文本生成请求:', req.body); // 直接将前端传来的body转发给GPU API const result = await generateText(req.body); res.json({ success: true, data: result }); } catch (error) { console.error('文本生成接口错误:', error); res.status(500).json({ success: false, message: error.message || '文本生成服务暂时不可用' }); } }); /** * 文件处理接口(单文件上传示例) * POST /api/process/file * 前端需要以 'file' 为字段名上传文件,同时可以传递其他JSON参数 */ router.post('/process/file', upload.single('file'), async (req, res) => { // req.file 包含了上传文件的信息 // req.body 包含了其他文本字段 if (!req.file) { return res.status(400).json({ success: false, message: '请上传文件' }); } const filePath = req.file.path; const additionalParams = { ...req.body }; // 复制其他参数 try { console.log(`处理文件: ${req.file.originalname}, 路径: ${filePath}`); const result = await processWithFile(filePath, additionalParams); // 处理完成后,可以选择删除临时文件以节省空间 await fs.unlink(filePath); console.log(`已删除临时文件: ${filePath}`); res.json({ success: true, data: result }); } catch (error) { // 即使出错,也尝试清理临时文件 try { await fs.unlink(filePath); } catch (e) { /* 忽略删除错误 */ } console.error('文件处理接口错误:', error); res.status(500).json({ success: false, message: error.message || '文件处理服务暂时不可用' }); } }); module.exports = router;4.2 在主应用中挂载路由
回到app.js,我们需要引入并使用刚才创建的路由。
// app.js (在原有代码上添加) const express = require('express'); const dotenv = require('dotenv'); const apiRoutes = require('./routes/apiRoutes'); // 新增:引入路由 dotenv.config(); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 挂载API路由,所有路由将以 `/api` 开头 app.use('/api', apiRoutes); // 新增:使用路由 app.get('/', (req, res) => { res.json({ message: 'Wan2.1-UMT5 API 代理服务运行正常!' }); }); app.listen(PORT, () => { console.log(`🚀 代理服务已启动,监听端口:${PORT}`); console.log(`📡 本地访问地址:http://localhost:${PORT}`); });5. 测试与调用示例
服务写好了,我们来试试它能不能正常工作。
5.1 启动服务并测试文本接口
确保服务在运行 (npm run dev)。我们可以用curl或者 Postman 来测试。
测试根路径:
curl http://localhost:3000应该返回{"message":"Wan2.1-UMT5 API 代理服务运行正常!"}。
测试文本生成接口:你需要根据 Wan2.1-UMT5 模型实际的API文档来构造请求体。这里是一个假设的示例:
curl -X POST http://localhost:3000/api/generate/text \ -H "Content-Type: application/json" \ -d '{ "prompt": "请用中文写一篇关于夏日星空的简短散文。", "max_tokens": 150, "temperature": 0.7 }'如果一切配置正确,这个请求会被我们的代理服务转发到星图GPU平台,并将模型的返回结果原样带回给你。
5.2 测试文件上传接口
测试文件接口稍微复杂一点,我们可以写一个简单的Node.js测试脚本,或者用Postman这样的工具。
创建一个简单的测试脚本test-upload.js:
// test-upload.js const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); async function testFileUpload() { const formData = new FormData(); // 假设我们有一个叫 test.jpg 的图片文件 formData.append('file', fs.createReadStream('./test.jpg')); // 附加其他参数,例如处理指令 formData.append('instruction', '请描述这张图片的内容'); try { const response = await axios.post('http://localhost:3000/api/process/file', formData, { headers: { ...formData.getHeaders(), }, }); console.log('上传成功:', JSON.stringify(response.data, null, 2)); } catch (error) { console.error('上传失败:', error.response?.data || error.message); } } testFileUpload();运行前记得在项目根目录放一个test.jpg文件,并安装axios (npm install axios)。然后运行node test-upload.js。观察服务器日志和返回结果,就能知道文件上传和处理是否成功。
6. 总结与后续优化建议
走完这一遍,一个基础的Node.js API代理服务就搭建完成了。前端同学现在只需要关心http://localhost:3000/api下的几个接口,完全不用管后端的GPU平台API长什么样、密钥怎么管理。这大大降低了集成难度,也提升了安全性。
实际用起来,你可能会发现一些可以打磨的地方。比如,可以增加请求参数验证,确保前端传过来的数据格式是对的;或者引入日志库,把请求和错误记录得更详细,方便排查问题。如果调用量大了,可以考虑增加缓存层,对相同的请求返回缓存结果,减轻GPU服务的压力。安全性方面,除了用环境变量保护密钥,还可以考虑增加API调用频率限制、或者添加简单的API Token认证。
这个服务只是一个起点,你可以根据Wan2.1-UMT5模型具体的功能(比如是不是还有语音生成、视频理解等),来添加更多的路由和封装函数。核心思路就是把复杂的、多变的后端服务,封装成一套简单、稳定、安全的接口给前端用,这个模式在很多AI应用集成里都非常实用。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。