【前后端的那些事】上传视频组件(包含前后端代码)

61 阅读3分钟

本篇文章提供现成的前端代码+后端代码,实现视频内容上传

演示

image.png

image.png

前端代码

考虑到该组件适用性,采用HTML + CDN引入的方式编写代码。核心组件为el-upload,该组件会自动上传用户选择的视频内容

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>视频上传平台</title>
    <script src="https://unpkg.com/vue@3"></script>
    <!-- import CSS -->
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
    <!-- import JavaScript -->
    <script src="https://unpkg.com/element-plus"></script>
    <script src="https://unpkg.com/element-plus"></script>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<div id="app">
    <div class="main-container">
        <div class="upload-card">
            <div class="header">
                <i class="fas fa-video icon"></i>
                <h1>视频上传中心</h1>
            </div>

            <el-upload
                    class="custom-upload"
                    action="http://localhost:8080/api/upload/video"
                    :on-success="handleSuccess"
                    :on-error="handleError"
                    :before-upload="beforeUpload"
                    :limit="5"
                    accept="video/*"
                    :auto-upload="true"
                    :file-list="fileList"
                    drag
            >
                <div class="upload-area">
                    <i class="fas fa-cloud-upload-alt cloud-icon"></i>
                    <div class="upload-text">
                        <p>拖放文件到这里 或</p>
                        <el-button type="primary" class="upload-button">
                            <i class="fas fa-upload button-icon"></i>选择文件
                        </el-button>
                    </div>
                </div>
                <template #tip>
                    <div class="upload-tip">
                        <i class="fas fa-info-circle tip-icon"></i>
                        支持格式:MP4/AVI/MOV,最大500MB
                    </div>
                </template>
            </el-upload>
        </div>
    </div>
</div>

<script>
    const { createApp } = Vue;
    const { ElUpload, ElButton, ElMessage } = ElementPlus;

    createApp({
        data() {
            return { fileList: [] };
        },
        methods: {
            handleSuccess(response) {
                ElMessage({
                    message: '上传成功!视频路径:' + response.filePath,
                    type: 'success',
                    duration: 5000,
                    showClose: true
                });
            },
            handleError() {
                ElMessage.error('上传失败,请检查网络或文件格式');
            },
            beforeUpload(file) {
                const isVideo = file.type.startsWith('video/');
                const isLt500M = file.size / 1024 / 1024 < 500;

                if (!isVideo) {
                    ElMessage.error('仅支持视频文件');
                    return false;
                }
                if (!isLt500M) {
                    ElMessage.error('文件大小超过500MB限制');
                    return false;
                }
                return true;
            }
        }
    })
        .use(ElementPlus)
        .mount('#app');
</script>

<style>
    body {
        background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
        min-height: 100vh;
        margin: 0;
        font-family: 'Helvetica Neue', Arial, sans-serif;
    }

    .main-container {
        display: flex;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
        padding: 20px;
    }

    .upload-card {
        background: white;
        border-radius: 16px;
        box-shadow: 0 8px 30px rgba(0,0,0,0.12);
        padding: 40px;
        width: 100%;
        max-width: 600px;
        transition: transform 0.3s ease;
    }

    .upload-card:hover {
        transform: translateY(-5px);
    }

    .header {
        text-align: center;
        margin-bottom: 30px;
    }

    .icon {
        font-size: 48px;
        color: #409EFF;
        margin-bottom: 15px;
    }

    h1 {
        color: #2c3e50;
        margin: 0;
        font-weight: 600;
    }

    .upload-area {
        padding: 40px 20px;
        border: 2px dashed #e0e0e0;
        border-radius: 12px;
        transition: all 0.3s ease;
    }

    .upload-area:hover {
        border-color: #409EFF;
        background: #f8f9ff;
    }

    .cloud-icon {
        font-size: 64px;
        color: #409EFF;
        margin-bottom: 20px;
        opacity: 0.8;
    }

    .upload-text {
        font-size: 16px;
        color: #666;
        line-height: 1.6;
    }

    .upload-button {
        margin-top: 15px;
        padding: 12px 28px;
        font-size: 16px;
        letter-spacing: 0.5px;
        transition: all 0.3s ease;
    }

    .button-icon {
        margin-right: 8px;
    }

    .upload-tip {
        margin-top: 20px;
        color: #909399;
        font-size: 14px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .tip-icon {
        margin-right: 8px;
        color: #409EFF;
    }

    @media (max-width: 768px) {
        .upload-card {
            padding: 25px;
            border-radius: 12px;
        }

        .cloud-icon {
            font-size: 48px;
        }
    }
</style>
</body>
</html>

后端代码

依然是考虑读者cv代码的方便程度,因此将所有逻辑写入Controller。需要注意的是,当前代码存储视频路径被写死,通过UPLOAD_DIR指定。读者可根据实际情况改变存储方式,譬如oss对象存储

@RestController
@RequestMapping("/api/upload")
public class VideoUploadController {

    // 获取 resources 目录的真实路径
    private static final String UPLOAD_DIR = "E:\java_code\video-upload\src\main\resources\uploads";

    @PostMapping("/video")
    @CrossOrigin(origins = "*")
    public ResponseEntity<Map<String, Object>> uploadVideo(@RequestParam("file") MultipartFile file) {
        Map<String, Object> response = new HashMap<>();

        if (file.isEmpty()) {
            response.put("message", "文件为空,请选择一个文件!");
            return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
        }

        String fileName = file.getOriginalFilename();
        if (fileName == null || !fileName.toLowerCase().matches(".*\.(mp4|avi|mov|mkv)$")) {
            response.put("message", "只能上传视频文件(支持格式:MP4、AVI、MOV、MKV)!");
            return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
        }

        try {
            // 获取 resources 目录的真实路径
            File uploadDir = new File(UPLOAD_DIR);

            // 如果目录不存在,则创建
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }

            // 构建文件路径
            Path filePath = Paths.get(uploadDir.getAbsolutePath(), fileName);

            // 如果文件存在,则删除
            if (filePath.toFile().exists()) {
                filePath.toFile().delete();
            }

            // 将文件保存到指定路径
            Files.copy(file.getInputStream(), filePath);

            response.put("message", "视频上传成功!");
            response.put("fileName", fileName);
            response.put("filePath", filePath.toString());
            return new ResponseEntity<>(response, HttpStatus.OK);

        } catch (IOException e) {
            e.printStackTrace();
            response.put("message", "视频上传失败:" + e.getMessage());
            return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}