1. 微信小程序视频上传功能概述
微信小程序视频上传是一个常见的功能需求,无论是社交分享、内容发布还是电商展示,都可能会用到。这个功能看似简单,但实际开发中会遇到各种细节问题。我做过不少小程序项目,发现视频上传功能最容易在文件大小限制、格式兼容性和上传稳定性这几个环节出问题。
微信官方提供了两个核心API来实现这个功能:wx.chooseVideo用于选择或拍摄视频,wx.uploadFile负责将视频上传到服务器。这两个API配合使用,就能完成从本地到服务器的完整上传流程。不过在实际项目中,我们还需要考虑很多优化点,比如上传进度显示、断点续传、压缩处理等。
2. 前端实现:视频选择与拍摄
2.1 使用wx.chooseVideo API
wx.chooseVideo是小程序提供的媒体选择API,它可以调起手机相册选择视频,或者直接启动相机拍摄。这个API有几个重要参数需要了解:
wx.chooseVideo({ sourceType: ['album', 'camera'], // 来源:相册和相机 maxDuration: 60, // 最长60秒 camera: 'back', // 默认后置摄像头 compressed: true, // 是否压缩 success(res) { console.log('临时文件路径:', res.tempFilePath) console.log('视频时长:', res.duration) console.log('视频大小:', res.size) } })我在项目中遇到过一个问题:用户上传的视频经常超出大小限制。后来我加了文件大小检查,如果超过100MB就提示用户重新选择:
let size = parseFloat(res.size/1024/1024).toFixed(1) if(parseFloat(size) > 100) { wx.showToast({ title: `视频大小超限,超出${parseFloat(size)-100}MB`, icon: 'none' }) return }2.2 视频预览与封面处理
选择视频后,通常需要给用户一个预览的机会。可以用小程序自带的video组件展示:
<video src="{{videoUrl}}" controls style="width: 100%;" ></video>实际开发中我发现,iOS和Android对视频格式的支持有差异。MP4格式兼容性最好,建议在上传前检查格式,必要时提醒用户转换。
3. 后端准备:服务器接口配置
3.1 设计上传接口
服务器端需要提供一个接收文件的接口。以Node.js为例,使用express和multer中间件:
const express = require('express') const multer = require('multer') const upload = multer({ dest: 'uploads/' }) app.post('/upload/video', upload.single('file'), (req, res) => { // 获取上传的文件信息 const file = req.file if(!file) { return res.status(400).send('未上传文件') } // 返回文件存储路径 res.json({ code: 200, videoUrl: `/videos/${file.filename}`, msg: '上传成功' }) })3.2 文件存储策略
我建议将视频文件存储在专门的云存储服务上,比如阿里云OSS或腾讯云COS。这样可以减轻服务器压力,也便于CDN加速。配置示例:
const OSS = require('ali-oss') const client = new OSS({ region: 'oss-cn-shanghai', accessKeyId: '你的accessKey', accessKeySecret: '你的accessSecret', bucket: '你的bucket名称' }) async function uploadToOSS(filePath) { try { const result = await client.put(`videos/${Date.now()}.mp4`, filePath) return result.url } catch(e) { console.error('上传OSS失败:', e) throw e } }4. 文件上传实现与优化
4.1 使用wx.uploadFile上传
wx.uploadFile是小程序的文件上传API,它支持显示上传进度:
const uploadTask = wx.uploadFile({ url: 'https://yourdomain.com/upload/video', filePath: tempFilePath, name: 'file', formData: { 'customData': '额外参数' }, success(res) { const data = JSON.parse(res.data) if(data.code === 200) { wx.showToast({ title: '上传成功' }) } } }) // 监听上传进度 uploadTask.onProgressUpdate((res) => { console.log(`上传进度: ${res.progress}%`) wx.showLoading({ title: `上传中 ${res.progress}%`, mask: true }) })4.2 上传优化技巧
在实际项目中,我总结了几个优化点:
- 分片上传:大文件分片上传,避免超时失败
- 断点续传:记录已上传的部分,网络恢复后继续
- 压缩处理:前端压缩视频,减少上传体积
- 格式转换:统一转换为兼容性好的MP4格式
分片上传的实现思路:
// 伪代码示例 async function chunkUpload(file, chunkSize = 5 * 1024 * 1024) { const chunks = Math.ceil(file.size / chunkSize) for(let i = 0; i < chunks; i++) { const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize) await uploadChunk(chunk, i, chunks) } await mergeChunks() }5. 常见问题与解决方案
5.1 文件大小限制问题
小程序对上传文件有默认限制,可以通过以下方式解决:
- 修改服务器配置,增大上传限制(如Nginx的client_max_body_size)
- 前端分片上传
- 压缩视频后再上传
5.2 上传超时处理
网络不稳定可能导致上传超时,我的解决方案是:
- 设置合理的超时时间
- 实现自动重试机制
- 提供暂停/继续上传功能
// 重试机制示例 async function uploadWithRetry(task, maxRetry = 3) { let retryCount = 0 while(retryCount < maxRetry) { try { await task() break } catch(e) { retryCount++ if(retryCount >= maxRetry) throw e await sleep(2000) // 等待2秒后重试 } } }5.3 跨域问题
如果服务器和小程序域名不同,需要配置CORS:
# Nginx配置示例 location / { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type'; }6. 安全与性能考量
6.1 安全措施
- 文件类型校验:检查文件头信息,防止伪装文件
- 大小限制:防止超大文件攻击
- 频率限制:防止恶意刷接口
- 权限验证:上传前检查用户权限
Node.js校验文件类型的示例:
const fileType = require('file-type') async function validateFile(buffer) { const type = await fileType.fromBuffer(buffer) if(!['mp4', 'mov'].includes(type.ext)) { throw new Error('不支持的文件格式') } }6.2 性能优化
- CDN加速:使用CDN分发视频
- 压缩转码:服务端自动转码为多种分辨率
- 懒加载:列表页只加载封面,详情页再加载视频
- 缓存策略:合理设置缓存头
7. 完整实现示例
7.1 前端完整代码
Page({ data: { videoUrl: '', uploadProgress: 0 }, chooseVideo() { wx.chooseVideo({ sourceType: ['album', 'camera'], maxDuration: 60, compressed: true, success: res => { const sizeMB = res.size / 1024 / 1024 if(sizeMB > 100) { wx.showToast({ title: '视频不能超过100MB', icon: 'none' }) return } this.uploadVideo(res.tempFilePath) } }) }, uploadVideo(filePath) { const uploadTask = wx.uploadFile({ url: 'https://yourdomain.com/upload', filePath, name: 'video', success: res => { const data = JSON.parse(res.data) if(data.code === 200) { this.setData({ videoUrl: data.url }) } } }) uploadTask.onProgressUpdate(res => { this.setData({ uploadProgress: res.progress }) }) } })7.2 后端完整代码(Node.js)
const express = require('express') const multer = require('multer') const fs = require('fs') const path = require('path') const app = express() const upload = multer({ dest: 'uploads/', limits: { fileSize: 100 * 1024 * 1024 } // 100MB }) app.post('/upload', upload.single('video'), (req, res) => { if(!req.file) { return res.status(400).json({ code: 400, msg: '未上传文件' }) } // 移动到永久存储目录 const newPath = path.join('public/videos', Date.now() + path.extname(req.file.originalname)) fs.renameSync(req.file.path, newPath) res.json({ code: 200, url: '/videos/' + path.basename(newPath) }) }) app.listen(3000, () => console.log('Server running on port 3000'))8. 进阶功能扩展
8.1 视频压缩处理
可以使用FFmpeg进行服务端压缩:
# 压缩视频命令示例 ffmpeg -i input.mp4 -vcodec libx264 -crf 28 -preset fast -acodec aac output.mp48.2 视频截图封面
生成视频封面图:
const ffmpeg = require('fluent-ffmpeg') function generateThumbnail(videoPath, outputPath) { return new Promise((resolve, reject) => { ffmpeg(videoPath) .screenshots({ timestamps: ['00:00:01'], filename: 'thumbnail.jpg', folder: path.dirname(outputPath) }) .on('end', resolve) .on('error', reject) }) }8.3 视频水印添加
给视频添加水印:
ffmpeg -i input.mp4 -i watermark.png -filter_complex "overlay=10:10" output.mp49. 测试与调试技巧
9.1 真机调试要点
- 不同机型测试,特别是iOS和Android的差异
- 网络环境测试:WiFi、4G、弱网环境
- 大文件上传测试
- 中断恢复测试
9.2 常见调试方法
- 使用微信开发者工具的Network面板查看请求
- 查看服务端日志
- 添加详细的客户端日志
- 使用Charles等工具抓包分析
// 添加详细日志 wx.chooseVideo({ success(res) { console.log('视频信息:', { path: res.tempFilePath, size: res.size, duration: res.duration }) }, fail(err) { console.error('选择视频失败:', err) } })10. 最佳实践总结
经过多个项目的实践,我总结了以下经验:
- 前端做好文件大小和格式校验,提前拦截不合格视频
- 显示上传进度,提升用户体验
- 服务端做好安全防护,防止恶意上传
- 大文件一定要用分片上传
- 使用云存储服务,不要直接存在应用服务器
- 做好错误处理和日志记录
- 考虑视频处理队列,避免同步处理耗时操作
视频上传功能看似简单,但要做一个健壮的实现需要考虑很多细节。我在实际项目中遇到过用户上传2小时长视频导致服务器存储爆满的情况,也遇到过恶意上传可执行文件的安全问题。这些经验教训让我意识到,一个好的上传功能不仅要有完整的主流程,还需要完善的边界处理和防护措施。