若依框架实战:手把手教你搞定视频上传与预览(Vue3 + Element Plus版)
若依框架实战Vue3Element Plus视频上传与预览全流程解析在Vue技术栈升级浪潮中若依框架Ruoyi作为企业级快速开发平台其Vue3Element Plus版本带来了显著的性能提升和开发体验优化。本文将深入探讨如何基于新版技术栈实现视频上传与预览功能解决开发者在迁移过程中遇到的实际问题。1. 环境准备与项目配置1.1 初始化若依Vue3项目首先确保已安装Node.js 16版本然后通过以下命令创建项目npm init vuelatest ruoyi-vue3-video --template vue-ts cd ruoyi-vue3-video npm install安装Element Plus及相关依赖npm install element-plus element-plus/icons-vue axios1.2 配置基础请求模块在src/utils/request.ts中配置axios实例import axios from axios import { getToken } from //utils/auth const service axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, timeout: 5000 }) service.interceptors.request.use(config { if (getToken()) { config.headers[Authorization] Bearer getToken() } return config })2. 视频上传组件重构2.1 Composition API实现核心逻辑使用Vue3的setup语法糖重构上传逻辑script setup langts import { ref } from vue import { ElMessage } from element-plus import { getToken } from //utils/auth const uploadUrl import.meta.env.VITE_APP_BASE_API /common/upload const headers { Authorization: Bearer getToken() } const videoUrl ref() const uploadProgress ref(0) const isUploading ref(false) const beforeUpload (file: File) { const validTypes [video/mp4, video/ogg, video/webm] const isLt50M file.size / 1024 / 1024 50 if (!validTypes.includes(file.type)) { ElMessage.error(仅支持MP4/OGG/WEBM格式) return false } if (!isLt50M) { ElMessage.error(视频大小不能超过50MB) return false } isUploading.value true return true } const handleProgress (event: ProgressEvent) { uploadProgress.value Math.round((event.loaded / event.total) * 100) } const handleSuccess (response: any) { videoUrl.value response.data.url isUploading.value false uploadProgress.value 0 } /script2.2 模板结构优化采用Element Plus的Upload组件实现更优雅的UItemplate el-upload classvideo-uploader :actionuploadUrl :headersheaders :show-file-listfalse :before-uploadbeforeUpload :on-progresshandleProgress :on-successhandleSuccess video v-ifvideoUrl :srcvideoUrl controls classvideo-preview / el-icon v-else classupload-icon Plus / /el-icon el-progress v-ifisUploading typecircle :percentageuploadProgress classupload-progress / /el-upload /template3. 后端接口适配与优化3.1 文件上传接口调整Spring Boot后端需要调整文件接收方式RestController RequestMapping(/common) public class FileUploadController { PostMapping(/upload) public AjaxResult uploadFile( RequestParam(file) MultipartFile file, HttpServletRequest request) { try { String originalName file.getOriginalFilename(); String extension originalName.substring(originalName.lastIndexOf(.)); if (!Arrays.asList(.mp4, .ogg, .webm).contains(extension)) { return AjaxResult.error(不支持的文件格式); } String fileName UUID.randomUUID() extension; String filePath RuoYiConfig.getUploadPath() fileName; File dest new File(filePath); file.transferTo(dest); String url ServletUtils.getRequestBaseUrl(request) /profile/upload/ fileName; return AjaxResult.success().put(url, url); } catch (Exception e) { return AjaxResult.error(e.getMessage()); } } }3.2 跨域与文件大小配置在application.yml中添加配置spring: servlet: multipart: max-file-size: 50MB max-request-size: 50MB ruoyi: profile: /path/to/upload4. 高级功能实现4.1 视频预览优化实现封面图生成与预览script setup import { ref, watch } from vue const videoRef refHTMLVideoElement | null(null) const posterUrl ref() watch(videoUrl, (url) { if (url) { generatePoster() } }) const generatePoster () { if (videoRef.value) { const canvas document.createElement(canvas) canvas.width 300 canvas.height 200 const ctx canvas.getContext(2d) videoRef.value.addEventListener(loadeddata, () { ctx?.drawImage(videoRef.value, 0, 0, canvas.width, canvas.height) posterUrl.value canvas.toDataURL(image/jpeg) }) } } /script4.2 分片上传实现对于大文件上传可采用分片上传策略const chunkSize 5 * 1024 * 1024 // 5MB const uploadByChunks async (file: File) { const chunks Math.ceil(file.size / chunkSize) const fileMd5 await calculateFileMd5(file) for (let i 0; i chunks; i) { const start i * chunkSize const end Math.min(file.size, start chunkSize) const chunk file.slice(start, end) const formData new FormData() formData.append(file, chunk) formData.append(chunkNumber, i.toString()) formData.append(totalChunks, chunks.toString()) formData.append(identifier, fileMd5) await axios.post(/common/chunk-upload, formData, { headers: { Content-Type: multipart/form-data } }) uploadProgress.value Math.round(((i 1) / chunks) * 100) } await axios.post(/common/merge, { filename: file.name, identifier: fileMd5, totalChunks: chunks }) }5. 性能优化与错误处理5.1 上传队列管理实现并发控制和失败重试机制class UploadQueue { private maxConcurrent 3 private queue: UploadTask[] [] private activeCount 0 add(task: UploadTask) { this.queue.push(task) this.run() } private async run() { if (this.activeCount this.maxConcurrent || this.queue.length 0) { return } this.activeCount const task this.queue.shift() try { await task.execute() } catch (error) { if (task.retryCount 3) { task.retryCount this.queue.unshift(task) } else { console.error(Upload failed after retries) } } finally { this.activeCount-- this.run() } } }5.2 类型安全增强为上传组件添加完整的TypeScript类型定义interface UploadResponse { code: number msg: string data: { url: string name: string size: number } } interface UploadProgressEvent { loaded: number total: number percent?: number } interface UploadRequestOptions { action: string file: File filename: string headers?: Recordstring, string onProgress?: (event: UploadProgressEvent) void onSuccess?: (response: UploadResponse) void onError?: (error: Error) void }在项目实际部署中我们发现视频格式检测不能仅依赖前端验证后端必须进行二次校验。同时对于移动端上传场景建议增加压缩选项和网络状态检测功能。