青核后端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

214 lines
7.8 KiB

package com.campus.controller;
import com.campus.common.Result;
import com.campus.dto.response.VodUploadCredential;
import com.campus.service.VodService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 阿里云 VOD 视频点播接口
*
* 上传流程:
* 1. POST /api/vod/upload → 前端发送视频文件 → 后端 CreateUploadVideo + 上传到 VOD Bucket
* 2. VOD 自动转码/审核 → 回调 POST /api/oss/vod-callback → 更新数据库
*
* 播放流程:
* 3. GET /api/vod/play-auth/{videoId} → 获取 PlayAuth → 前端初始化播放器
*/
@Slf4j
@RestController
@RequestMapping("/api/vod")
@RequiredArgsConstructor
public class VodController {
private final VodService vodService;
/**
* 上传视频到 VOD(一步完成:创建凭证 + 上传到 VOD Bucket)
*
* POST /api/vod/upload
* Content-Type: multipart/form-data
* 参数: file (视频文件)
*
* @return { videoId, requestId }
*/
@PostMapping("/upload")
public Result<Map<String, Object>> uploadVideo(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error(400, "文件不能为空");
}
String fileName = file.getOriginalFilename();
if (fileName == null) {
fileName = "video.mp4";
}
try {
// Step 1: 创建上传凭证(CreateUploadVideo)
log.info("开始VOD上传: fileName={}, size={}MB", fileName,
String.format("%.1f", file.getSize() / 1024.0 / 1024.0));
VodUploadCredential credential = vodService.createUploadVideo(fileName);
// Step 2: 使用临时凭证上传到 VOD Bucket
String videoId = vodService.uploadToVodBucket(
file.getInputStream(),
file.getSize(),
credential,
file.getContentType()
);
Map<String, Object> result = new HashMap<>();
result.put("videoId", videoId);
result.put("requestId", credential.getRequestId());
log.info("VOD上传完成: videoId={}", videoId);
return Result.success(result);
} catch (IOException e) {
log.error("VOD上传失败: 文件读取错误", e);
return Result.error(500, "文件读取失败: " + e.getMessage());
} catch (Exception e) {
log.error("VOD上传失败: fileName={}", fileName, e);
return Result.error(500, "视频上传失败: " + e.getMessage());
}
}
/**
* 上传图片到 VOD(一步完成:创建凭证 + 上传到 VOD Bucket)
*
* POST /api/vod/upload-image
* Content-Type: multipart/form-data
* 参数: file (图片文件)
*
* @return { imageId, imageUrl, requestId }
*/
@PostMapping("/upload-image")
public Result<Map<String, Object>> uploadImage(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error(400, "文件不能为空");
}
String fileName = file.getOriginalFilename();
if (fileName == null) {
fileName = "image.jpg";
}
try {
log.info("开始VOD图片上传: fileName={}, size={}KB", fileName,
String.format("%.1f", file.getSize() / 1024.0));
// Step 1: 创建上传凭证(CreateUploadImage)
VodUploadCredential credential = vodService.createUploadImage(fileName);
// Step 2: 使用临时凭证上传到 VOD Bucket
String imageId = vodService.uploadToVodBucket(
file.getInputStream(),
file.getSize(),
credential,
file.getContentType()
);
Map<String, Object> result = new HashMap<>();
result.put("imageId", imageId);
result.put("imageUrl", credential.getImageUrl());
result.put("requestId", credential.getRequestId());
log.info("VOD图片上传完成: imageId={}, imageUrl={}", imageId, credential.getImageUrl());
return Result.success(result);
} catch (IOException e) {
log.error("VOD图片上传失败: 文件读取错误", e);
return Result.error(500, "文件读取失败: " + e.getMessage());
} catch (Exception e) {
log.error("VOD图片上传失败: fileName={}", fileName, e);
return Result.error(500, "图片上传失败: " + e.getMessage());
}
}
/**
* 创建上传凭证(用于前端直传场景 — 高级用法,需要前端集成 OSS SDK)
*
* POST /api/vod/create-upload
* Body: { "fileName": "video.mp4" }
*
* 注意:React Native 环境下建议使用 POST /api/vod/upload,后端代理上传更稳定
*/
@PostMapping("/create-upload")
public Result<VodUploadCredential> createUpload(@RequestBody Map<String, String> body) {
String fileName = body.getOrDefault("fileName", "video.mp4");
try {
VodUploadCredential credential = vodService.createUploadVideo(fileName);
return Result.success(credential);
} catch (Exception e) {
log.error("创建上传凭证失败: fileName={}", fileName, e);
return Result.error(500, "创建上传凭证失败: " + e.getMessage());
}
}
/**
* 刷新上传凭证(凭证有效期 3000 秒)
*
* POST /api/vod/refresh-upload/{videoId}
*/
@PostMapping("/refresh-upload/{videoId}")
public Result<VodUploadCredential> refreshUpload(@PathVariable String videoId) {
try {
VodUploadCredential credential = vodService.refreshUploadVideo(videoId);
return Result.success(credential);
} catch (Exception e) {
log.error("刷新上传凭证失败: videoId={}", videoId, e);
return Result.error(500, "刷新上传凭证失败: " + e.getMessage());
}
}
/**
* 获取视频播放凭证(PlayAuth — 安全播放)
*
* GET /api/vod/play-auth/{videoId}
*
* 返回 PlayAuth,前端用它初始化阿里云播放器 SDK
* 同时返回视频元信息(封面、时长等)
*/
@GetMapping("/play-auth/{videoId}")
public Result<Map<String, Object>> getPlayAuth(@PathVariable String videoId) {
try {
String playAuth = vodService.getPlayAuth(videoId);
Map<String, Object> playInfo = vodService.getPlayInfo(videoId);
Map<String, Object> result = new HashMap<>();
result.put("videoId", videoId);
result.put("playAuth", playAuth);
result.put("coverUrl", playInfo.get("coverUrl"));
result.put("duration", playInfo.get("duration"));
return Result.success(result);
} catch (Exception e) {
log.error("获取播放凭证失败: videoId={}", videoId, e);
return Result.error(500, "获取播放凭证失败: " + e.getMessage());
}
}
/**
* 获取视频播放地址(简单场景,直接返回 m3u8 URL)
*
* GET /api/vod/play-info/{videoId}
*/
@GetMapping("/play-info/{videoId}")
public Result<Map<String, Object>> getPlayInfo(@PathVariable String videoId) {
try {
Map<String, Object> playInfo = vodService.getPlayInfo(videoId);
return Result.success(playInfo);
} catch (Exception e) {
log.error("获取播放信息失败: videoId={}", videoId, e);
return Result.error(500, "获取播放信息失败: " + e.getMessage());
}
}
}