AI图文转视频小程序开发实战:UniApp+Flask本地部署指南
2026/9/12 9:44:08 网站建设 项目流程

简介:这是一套面向开发者与AI应用实践者的微信小程序源码包,聚焦智能AI图文视频生成场景,适用于希望快速上线AI工具类小程序的中高级前端+后端开发者。资源包含完整前后端代码及详细安装教程,支持AI对话、英文翻译、智能绘图、图片生成、全网99+平台视频去水印及智能视频生成等六大核心功能,采用阿里云函数部署方案,兼顾轻量性与可扩展性。压缩包共1364个文件,以512个Python源码(含AI逻辑与后端服务)、510个编译字节码(pyc)、34个JS脚本(前端交互)、26个WXML/WXSS(小程序视图层)及29个JSON配置文件为主,辅以安装文档、样式表与二进制依赖,整体仅6.8MB,结构紧凑、模块清晰。目前已有1895人学习下载,读者可直接复用四大业务模块(智能AI、免费专区、优惠专区、我的中心),快速完成本地调试、云函数部署与数据库迁移,显著降低AI小程序从0到1的开发门槛。

1. 这不是“一键生成小程序”的营销噱头,而是用 AI 视觉能力驱动微信小程序内容生产的最小可行闭环

你下载的这个.rar包里,没有魔法按钮,也没有全自动部署云服务。它是一套可本地验证、可调试、可替换模型、可对接自有 API 的前后端分离结构:前端用 Vue 3 + UniApp 编写,适配微信小程序平台;后端基于 Python Flask 构建轻量服务,核心逻辑是接收用户上传的图片或文本提示(prompt),调用本地或远程的 Stable Diffusion 或类似图像生成模型,输出视频帧序列或 GIF,再封装为小程序可直接播放的mp4webp格式。整个流程不依赖微信官方 AI 接口,也不走任何第三方 SaaS 平台——这意味着你能控制生成质量、响应延迟、数据不出域,也意味着你必须亲手配置模型路径、调整推理参数、处理跨域请求、适配小程序wx.uploadFile的 multipart 上传规范。适合两类人:一是想快速验证 AI 图文转视频在小程序场景落地可行性的产品/技术负责人;二是前端已熟悉 UniApp、后端有 Python Web 开发经验,需要一个带完整上下文(含错误日志捕获、进度回调、失败重试)的参考项目来启动二次开发。


2. 前端用 UniApp 实现图片上传→AI 生成→视频预览的三步交互链路

2.1 初始化 UniApp 项目并适配微信小程序基础能力

该源码基于 HBuilderX 4.20+ 或 CLI 创建的标准 UniApp 项目,manifest.json中已声明"mp-weixin"平台支持,并启用"webrtc""camera"权限(因部分生成任务需调用摄像头实时取帧)。关键配置项如下:

{ "name": "ai-video-gen", "appid": "", "description": "", "versionName": "1.0.0", "versionCode": "100", "transformPx": false, "app-plus": { "usingComponents": true }, "mp-weixin": { "usingComponents": true, "permission": { "scope.userLocation": { "desc": "用于获取位置信息" }, "scope.camera": { "desc": "用于拍摄图片触发生成" } } } }

提示:若使用 VS Code 开发,需安装uni-app官方插件,并在vue.config.js中添加configureWebpack配置,禁用mini-css-extract-plugin.wxss的提取,否则真机调试时样式丢失。

2.2 图片上传与 prompt 输入的双通道设计

小程序首页pages/index/index.vue中,提供两种触发方式:

  • 相册选择:调用uni.chooseImage,限制count: 1sizeType: ['compressed'],确保上传体积可控;
  • 文字描述输入<input>绑定v-model="prompt",配合placeholder="例如:一只橘猫坐在窗台,阳光洒落,4K高清",长度限制 120 字符。

上传逻辑封装为uploadMedia()方法,核心代码如下:

async uploadMedia() { if (!this.prompt && !this.tempFilePath) { uni.showToast({ title: '请先选择图片或输入描述', icon: 'none' }); return; } const formData = new FormData(); if (this.tempFilePath) { // 小程序不支持直接 append File 对象,需用临时路径转 base64 后构造 Blob const file = await this.getBase64FromFile(this.tempFilePath); const blob = this.base64ToBlob(file); formData.append('image', blob, 'input.jpg'); } formData.append('prompt', this.prompt); formData.append('style', this.selectedStyle); // 如 'anime', 'realistic' try { const res = await uni.uploadFile({ url: 'https://localhost:5000/generate', filePath: this.tempFilePath || '', name: 'image', formData, header: { 'Content-Type': 'multipart/form-data' }, success: (uploadRes) => { const data = JSON.parse(uploadRes.data); if (data.status === 'success') { this.videoUrl = data.video_url; // 后端返回 CDN 路径或 base64 data URL this.isGenerating = false; } }, fail: (err) => { console.error('上传失败', err); uni.showToast({ title: '生成请求失败,请检查后端是否运行', icon: 'none' }); } }); } catch (e) { console.error(e); } },
2.2.1 关键细节说明
  • getBase64FromFile()使用uni.getFileSystemManager().readFile读取临时文件,再通过uni.arrayBufferToBase64转换,避免wx.chooseImage返回的tempFilePathuploadFile中被拒绝;
  • base64ToBlob()是必要转换:微信小程序uploadFile不接受纯 base64 字符串作为文件字段,必须构造Blob对象;
  • formData.append('image', blob, 'input.jpg')中的文件名必须含扩展名,否则后端 Flask 的request.files.get('image')无法识别 MIME 类型;
  • header中设置'Content-Type': 'multipart/form-data'是冗余但必要的——尽管uploadFile自动设置 boundary,但显式声明可避免某些 Nginx 反向代理拦截。

2.3 视频播放与加载状态管理

生成结果以video_url形式返回,可能是:

  • 本地http://localhost:5000/static/output/xxx.mp4(开发环境);
  • 或 OSS/CDN 地址如https://my-bucket.oss-cn-hangzhou.aliyuncs.com/videos/xxx.mp4(生产环境)。

前端使用<video>组件播放,并监听@error事件处理 404:

<video v-if="videoUrl" :src="videoUrl" controls autoplay @error="onVideoError" style="width: 100%; height: 300px;" />
onVideoError() { uni.showToast({ title: '视频加载失败,请重试', icon: 'none' }); this.videoUrl = ''; }

注意:微信小程序video组件对https协议强制校验证书,若使用自签名证书的本地后端,需在project.config.json中开启"miniprogramRoot": "./", "setting": { "urlCheck": false }(仅开发阶段),否则video加载空白且无报错。


3. 后端用 Flask 搭建轻量 AI 服务,支持图片/文本双模态输入与视频合成

3.1 Flask 服务结构与依赖管理

后端目录结构清晰分层:

backend/ ├── app.py # 主应用入口,含路由定义 ├── config.py # 配置类,区分 dev/prod ├── models/ │ ├── __init__.py │ ├── diffusion.py # 封装 Stable Diffusion 加载与推理 │ └── video.py # 视频帧合成与编码逻辑 ├── utils/ │ ├── file_handler.py # 文件存储、清理、路径生成 │ └── logger.py # 结构化日志(含 request_id 追踪) └── requirements.txt

requirements.txt关键依赖(经实测兼容性验证):

Flask==2.3.3 torch==2.1.0+cpu torchvision==0.16.0+cpu diffusers==0.23.0 transformers==4.35.0 opencv-python==4.8.1.78 moviepy==2.0.0 Pillow==10.1.0

提示:若服务器有 NVIDIA GPU,将torch替换为torch==2.1.0+cu118并安装对应cudnn,推理速度提升 3~5 倍;moviepy依赖ffmpeg,需提前apt install ffmpeg或 Windows 下配置ffmpeg.exe环境变量。

3.2/generate接口实现:接收、校验、调度、返回

app.py中核心路由如下:

@app.route('/generate', methods=['POST']) def generate_video(): try: # 1. 解析 multipart 表单 image_file = request.files.get('image') prompt = request.form.get('prompt', '').strip() style = request.form.get('style', 'realistic') # 2. 校验输入 if not image_file and not prompt: return jsonify({'status': 'error', 'message': '至少提供图片或文字描述'}), 400 if len(prompt) > 120: return jsonify({'status': 'error', 'message': '描述长度不能超过120字符'}), 400 # 3. 保存上传图片(若存在) input_path = None if image_file: filename = secure_filename(image_file.filename) input_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{uuid.uuid4().hex}_{filename}") image_file.save(input_path) # 4. 调用生成服务 result = generate_from_input(input_path, prompt, style) # 5. 构造响应 if result['success']: return jsonify({ 'status': 'success', 'video_url': f"http://localhost:5000/static/output/{result['video_name']}", 'duration_ms': result['duration'] }) else: return jsonify({'status': 'error', 'message': result['error']}), 500 except Exception as e: app.logger.error(f"Generate endpoint error: {str(e)}") return jsonify({'status': 'error', 'message': '服务内部错误'}), 500
3.2.1generate_from_input()的关键参数控制表
参数类型默认值说明可调范围
num_inference_stepsint30扩散步数,影响质量与耗时15–50
guidance_scalefloat7.5文本引导强度,越高越贴合 prompt1.0–20.0
frame_rateint12输出视频帧率,影响流畅度与体积8–24
output_duration_secfloat3.0视频总时长(秒)1.0–10.0
model_namestr"runwayml/stable-diffusion-v1-5"Hugging Face 模型 ID支持stabilityai/sd-x2-latent-upscaler

这些参数在models/diffusion.py中通过pipeline.__call__()传入,例如:

images = pipe( prompt=prompt, image=input_image if input_image else None, num_inference_steps=30, guidance_scale=7.5, width=512, height=512, output_type="pil" ).images

注意:width/height必须为 64 的倍数,否则diffusers报错;output_type="pil"是必须的,因后续moviepy需 PIL Image 对象合成视频。

3.3 视频合成与静态资源托管

生成的 PIL 图像列表交由models/video.py处理:

def create_video_from_images(images, output_path, fps=12, duration_sec=3.0): clips = [] for img in images: # 转为 numpy array 并适配 moviepy frame = np.array(img) clip = ImageClip(frame).set_duration(duration_sec / len(images)) clips.append(clip) final_clip = concatenate_videoclips(clips, method="compose") final_clip.write_videofile( output_path, fps=fps, codec='libx264', audio=False, threads=2, preset='medium' # 平衡速度与压缩率 ) final_clip.close()

静态文件通过 Flasksend_from_directory提供:

@app.route('/static/<path:filename>') def static_files(filename): return send_from_directory(app.config['STATIC_FOLDER'], filename)

config.py中定义:

class Config: UPLOAD_FOLDER = os.path.join(os.getcwd(), 'uploads') STATIC_FOLDER = os.path.join(os.getcwd(), 'static') MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB 上传限制

提示:uploads/static/output/目录需手动创建,否则首次请求报FileNotFoundErrorMAX_CONTENT_LENGTH必须显式设置,否则大图上传直接 413 错误。


4. 安装教程:从解压到本地跑通,绕过 90% 的环境陷阱

4.1 前端安装:UniApp 项目初始化与依赖注入

  1. 解压.rar得到frontend/目录,进入该目录执行:

    npm install # 若提示 node-sass 编译失败,改用 dart-sass: npm uninstall node-sass && npm install sass
  2. 修改vue.config.js添加 alias(避免相对路径混乱)

    configureWebpack: { resolve: { alias: { '@': path.resolve(__dirname, 'src'), '@utils': path.resolve(__dirname, 'src/utils') } } }
  3. 启动开发服务

    # 方式一:HBuilderX 导入项目 → 运行 → 微信开发者工具 # 方式二:CLI 启动(需全局安装 @dcloudio/vue-cli-plugin-uni) npm run dev:mp-weixin

    输出Listening at http://localhost:8080后,在微信开发者工具中选择「本地小程序」→「导入项目」→ 指向frontend/dist/build/mp-weixin

4.2 后端安装:Python 环境隔离与模型缓存预热

  1. 创建独立虚拟环境(推荐venv,非 conda):

    python -m venv backend_env source backend_env/bin/activate # Linux/macOS # backend_env\Scripts\activate.bat # Windows
  2. 安装依赖并预热模型(首次运行耗时较长):

    cd backend pip install -r requirements.txt # 预加载模型(避免首次请求超时) python -c " from models.diffusion import load_pipeline pipe = load_pipeline('runwayml/stable-diffusion-v1-5') print('Pipeline loaded successfully') "
  3. 启动 Flask 服务

    export FLASK_APP=app.py export FLASK_ENV=development flask run --host=0.0.0.0 --port=5000

    若提示Address already in use,检查lsof -i :5000或改用--port=5001

4.3 前后端联调关键配置点

问题现象根本原因解决方案
小程序上传报request:fail network error前端uploadFile.url指向http://localhost:5000,但真机无法解析localhost修改pages/index/index.vueurl为局域网 IP(如http://192.168.1.100:5000/generate),并确保手机与电脑在同一 WiFi
后端返回400 Bad Request且日志显示no image or prompt小程序uploadFile未正确传递formData,或后端未启用request.files解析检查app.py是否漏写from flask import request;确认uploadFilename字段与后端request.files.get('image')的 key 一致
视频生成后黑屏或报invalid videomoviepy编码参数不兼容微信小程序强制指定codec='libx264'audio=False,删除bitrate参数(易导致 iOS 解码失败)
首次生成耗时超 2 分钟模型未预热,且diffusers默认启用safetensors校验load_pipeline()中添加safety_checker=Nonerequires_safety_checker=False

提示:Windows 用户若遇到OSError: [WinError 127] 找不到指定的程序,大概率是ffmpeg未加入 PATH,下载 ffmpeg.org 静态版,解压后将bin/目录加入系统环境变量。


5. 修改刚进入的加载页面:从骨架屏到 AI 生成进度可视化

5.1 替换默认启动页并注入实时进度状态

微信小程序启动页(app.jsonLaunch)默认为白屏,需在pages/index/index.vueonLoad钩子中主动控制:

onLoad() { this.showSplash = true; // 显示自定义骨架屏 setTimeout(() => { this.showSplash = false; this.isReady = true; // 进入主界面 }, 800); // 模拟资源加载,实际可结合 `uni.preload` 提前加载模型元数据 }

模板中添加骨架屏:

<view v-if="showSplash" class="splash"> <view class="logo"></view> <view class="text">AI 正在理解您的创意...</view> <view class="progress-bar"> <view class="progress-fill" :style="{ width: progress + '%' }"></view> </view> </view>

5.2 后端推送进度:用 Server-Sent Events(SSE)替代轮询

修改/generate接口为流式响应,后端app.py新增 SSE 路由:

@app.route('/progress/<task_id>') def stream_progress(task_id): def event_stream(): while True: progress = get_task_progress(task_id) # 从 Redis 或内存字典读取 if progress >= 100: yield f"data: {json.dumps({'progress': 100, 'status': 'done'})}\n\n" break yield f"data: {json.dumps({'progress': progress, 'status': 'processing'})}\n\n" time.sleep(0.5) return Response(event_stream(), mimetype="text/event-stream")

前端用EventSource监听:

const eventSource = new EventSource(`http://192.168.1.100:5000/progress/${taskId}`); eventSource.onmessage = (e) => { const data = JSON.parse(e.data); this.progress = data.progress; if (data.status === 'done') { eventSource.close(); } };
5.2.1 进度数据存储策略对比表
方案实现难度实时性适用场景注意事项
内存字典(task_progress = {}★☆☆☆☆单进程开发环境进程重启后丢失,需加锁防止并发写
RedisINCR+GET★★★☆☆多 worker 生产环境需安装redis-pytask_iduuid4()保证唯一
SQLiteUPDATE ... WHERE id=?★★☆☆☆无 Redis 的轻量部署频繁写入可能锁表,建议每秒更新 ≤5 次

选用内存字典即可满足本项目需求,get_task_progress()实现为:

task_progress = {} def update_progress(task_id, value): task_progress[task_id] = value def get_task_progress(task_id): return task_progress.get(task_id, 0)

注意:SSE 连接需在generate_video()中生成task_id并启动后台线程更新进度,主线程立即返回202 Accepted,前端收到task_id后发起EventSource请求——这是真正解耦“提交”与“轮询”的标准做法,比 setInterval 轮询更优雅、更低开销。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询