当前位置: 首页 > news >正文

若依框架实战:手把手教你搞定视频上传与预览(Vue3 + Element Plus版)

若依框架实战:Vue3+Element Plus视频上传与预览全流程解析

在Vue技术栈升级浪潮中,若依框架(Ruoyi)作为企业级快速开发平台,其Vue3+Element Plus版本带来了显著的性能提升和开发体验优化。本文将深入探讨如何基于新版技术栈实现视频上传与预览功能,解决开发者在迁移过程中遇到的实际问题。

1. 环境准备与项目配置

1.1 初始化若依Vue3项目

首先确保已安装Node.js 16+版本,然后通过以下命令创建项目:

npm init vue@latest ruoyi-vue3-video --template vue-ts cd ruoyi-vue3-video npm install

安装Element Plus及相关依赖:

npm install element-plus @element-plus/icons-vue axios

1.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 lang="ts"> 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 } </script>

2.2 模板结构优化

采用Element Plus的Upload组件实现更优雅的UI:

<template> <el-upload class="video-uploader" :action="uploadUrl" :headers="headers" :show-file-list="false" :before-upload="beforeUpload" :on-progress="handleProgress" :on-success="handleSuccess" > <video v-if="videoUrl" :src="videoUrl" controls class="video-preview" /> <el-icon v-else class="upload-icon"> <Plus /> </el-icon> <el-progress v-if="isUploading" type="circle" :percentage="uploadProgress" class="upload-progress" /> </el-upload> </template>

3. 后端接口适配与优化

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/upload

4. 高级功能实现

4.1 视频预览优化

实现封面图生成与预览:

<script setup> import { ref, watch } from 'vue' const videoRef = ref<HTMLVideoElement | 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') }) } } </script>

4.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?: Record<string, string> onProgress?: (event: UploadProgressEvent) => void onSuccess?: (response: UploadResponse) => void onError?: (error: Error) => void }

在项目实际部署中,我们发现视频格式检测不能仅依赖前端验证,后端必须进行二次校验。同时,对于移动端上传场景,建议增加压缩选项和网络状态检测功能。

http://www.cnnetsun.cn/news/2058302.html

相关文章:

  • real-anime-z Web界面深度解析:7860端口服务的UI功能区与技术栈说明
  • Scikit-Learn入门指南:Python机器学习核心技术与实战
  • ESP8266玩转LED:从硬件连接到代码调试的全流程指南(附常见问题排查)
  • 脐橙品质分级机的输送装置及单列化结构设计(说明书+cad图纸+Proe三维图形+答辩)
  • 告别卡顿!用SRIO(Serial RapidIO)在FPGA与DSP间搭建高速数据通道(附Xilinx平台实战)
  • 机器人应用-地下车库清洁
  • 拆解一台VPX-305加固机箱:聊聊3U VPX背板设计、电源选型与散热那些坑
  • 【YOLO系列】从Smooth L1到CIOU:目标检测损失函数演进与实战选型指南
  • LLM嵌入向量与传统聚类算法结合的文本分析实践
  • 手把手教你用Python脚本自动下载并转换香港CORS的RINEX数据(附Matlab工具替代方案)
  • 机器学习中不平衡分类问题的挑战与解决方案
  • 别再让扫码枪乱跳了!C# WinForm中TextBox光标定位的保姆级避坑指南
  • CFX用户必看:瞬态结果里提取‘区间平均值’的两种方法对比与避坑指南
  • 调试LVDS屏别再只改代码了!从屏闪、白屏到触屏漂移,三个实战问题背后的硬件时序与配置原理
  • NVIDIA cuOpt:GPU加速的物流路径优化引擎解析
  • 永磁同步电机谐波抑制实战:多同步旋转坐标系下五七次谐波电流的闭环抑制策略
  • 从“选择面”到“选择任何东西”:一个C# NXOpen SelectionType数组的万能配置指南
  • CentOS7服务器维护:除了reboot,这几种安全重启和关机命令你用过吗?
  • 模力方舟MoArk:重塑AI开发体验的一站式平台
  • 【中等】出现次数的TOPK问题-Java:原问题
  • ZynqMP启动文件BOOT.bin深度拆解:从FSBL、PMU到ATF,每个ELF文件都是干嘛的?
  • 【相当困难】Manacher算法-Java:进阶问题
  • Mastodon iOS:官方开源社交应用完全解析与入门指南
  • 为什么fastp比Trimmomatic快10倍?深度解析其核心算法原理
  • 如何快速上手radian:R语言开发者的终极控制台解决方案
  • 群晖NAS里的osheet文件打不开?用Python写个脚本,5分钟转成标准Excel
  • 深度学习三大架构:MLP、CNN与RNN核心差异与应用指南
  • Qwen3-4B-Instruct效果展示:法律条文关联引用自动标注与案例匹配
  • angular-formly进阶指南:自定义模板与验证消息的高级玩法
  • LLM Compressor量化算法对比:GPTQ、AWQ、SmoothQuant哪种更适合你的场景?