feat(material): remove video cover extraction and simplify upload API
- Remove extractVideoCoverOptional function and related video cover processing - Update MaterialService.uploadFile method signature to remove coverBase64 parameter - Simplify uploadAndIdentifyVideo function by removing cover generation logic - Remove loading indicator from VideoSelector component during video preview - Add presignGetUrlWithProcess method to FileClient interface for processed file URLs - Add logging support to S3FileClient implementation
This commit is contained in:
@@ -43,13 +43,11 @@ public class AppTikUserFileController {
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "文件分类(video/generate/audio/mix/voice)", required = true)
|
||||
@RequestParam("fileCategory") String fileCategory,
|
||||
@Parameter(description = "视频封面 base64(可选,data URI 格式)")
|
||||
@RequestParam(value = "coverBase64", required = false) String coverBase64,
|
||||
@Parameter(description = "视频时长(秒)")
|
||||
@RequestParam(value = "duration", required = false) Integer duration,
|
||||
@Parameter(description = "分组编号(可选)")
|
||||
@RequestParam(value = "groupId", required = false) Long groupId) {
|
||||
return success(userFileService.uploadFile(file, fileCategory, coverBase64, duration, groupId));
|
||||
return success(userFileService.uploadFile(file, fileCategory, duration, groupId));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
|
||||
@@ -59,14 +59,6 @@ public class TikUserFileDO extends TenantBaseDO {
|
||||
* 文件存储路径
|
||||
*/
|
||||
private String filePath;
|
||||
/**
|
||||
* 封面图URL(视频文件的封面图)
|
||||
*/
|
||||
private String coverUrl;
|
||||
/**
|
||||
* 封面图Base64(视频文件的封面图原始base64数据,可选)
|
||||
*/
|
||||
private String coverBase64;
|
||||
/**
|
||||
* 缩略图URL(图片文件的缩略图)
|
||||
*/
|
||||
|
||||
@@ -19,12 +19,11 @@ public interface TikUserFileService {
|
||||
*
|
||||
* @param file 文件
|
||||
* @param fileCategory 文件分类(video/generate/audio/mix/voice)
|
||||
* @param coverBase64 视频封面 base64(可选,data URI 格式)
|
||||
* @param duration 视频时长(秒,可选)
|
||||
* @param groupId 分组编号(可选)
|
||||
* @return 文件编号
|
||||
*/
|
||||
Long uploadFile(MultipartFile file, String fileCategory, String coverBase64, Integer duration, Long groupId);
|
||||
Long uploadFile(MultipartFile file, String fileCategory, Integer duration, Long groupId);
|
||||
|
||||
/**
|
||||
* 分页查询文件列表
|
||||
@@ -75,6 +74,17 @@ public interface TikUserFileService {
|
||||
*/
|
||||
String getCachedPresignUrl(String url, Integer expirationSeconds);
|
||||
|
||||
/**
|
||||
* 获取缓存的预签名URL(带 OSS 处理参数,带 Redis 缓存)
|
||||
* 用于阿里云 OSS 视频截帧等图片处理场景
|
||||
*
|
||||
* @param url 文件URL
|
||||
* @param expirationSeconds 过期时间(秒)
|
||||
* @param processParam OSS 处理参数,如 "video/snapshot,t_1000,f_jpg,w_300"
|
||||
* @return 预签名URL
|
||||
*/
|
||||
String getCachedPresignUrlWithProcess(String url, Integer expirationSeconds, String processParam);
|
||||
|
||||
/**
|
||||
* 获取预签名URL(直传模式)
|
||||
*
|
||||
|
||||
@@ -74,7 +74,7 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
private FileConfigService fileConfigService;
|
||||
|
||||
@Override
|
||||
public Long uploadFile(MultipartFile file, String fileCategory, String coverBase64, Integer duration, Long groupId) {
|
||||
public Long uploadFile(MultipartFile file, String fileCategory, Integer duration, Long groupId) {
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
|
||||
// 校验文件分类和配额
|
||||
@@ -88,7 +88,7 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
|
||||
// 保存数据库
|
||||
return saveFileRecord(userId, file, fileCategory, context.fileUrl, context.filePath,
|
||||
coverBase64, baseDirectory, context.infraFileId, duration, groupId);
|
||||
context.infraFileId, duration, groupId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,44 +179,19 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理视频封面上传
|
||||
*/
|
||||
private String handleCoverUpload(String coverBase64, String fileName, String fileType, String baseDirectory) {
|
||||
if (StrUtil.isBlank(coverBase64) || !StrUtil.containsIgnoreCase(fileType, "video")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String base64Data = coverBase64.contains(",")
|
||||
? coverBase64.substring(coverBase64.indexOf(",") + 1)
|
||||
: coverBase64;
|
||||
byte[] coverBytes = java.util.Base64.getDecoder().decode(base64Data);
|
||||
String coverFileName = fileName.replaceFirst("\\.[^.]+$", "_cover.jpg");
|
||||
String uploadedUrl = fileApi.createFile(coverBytes, coverFileName, baseDirectory, "image/jpeg");
|
||||
if (StrUtil.isNotBlank(uploadedUrl) && !uploadedUrl.contains("data:image")) {
|
||||
return HttpUtils.removeUrlQuery(uploadedUrl);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("[handleCoverUpload][视频封面处理失败: {}]", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件记录到数据库
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long saveFileRecord(Long userId, MultipartFile file, String fileCategory,
|
||||
String fileUrl, String filePath, String coverBase64,
|
||||
String baseDirectory, Long infraFileId, Integer duration, Long groupId) {
|
||||
String fileUrl, String filePath,
|
||||
Long infraFileId, Integer duration, Long groupId) {
|
||||
if (infraFileId == null) {
|
||||
log.error("[saveFileRecord][infra_file.id 为空,用户({}),URL({})]", userId, fileUrl);
|
||||
throw exception(FILE_NOT_EXISTS, "文件记录保存失败:无法获取文件ID");
|
||||
}
|
||||
|
||||
String fileName = file.getOriginalFilename();
|
||||
String fileType = file.getContentType();
|
||||
String coverUrl = handleCoverUpload(coverBase64, fileName, fileType, baseDirectory);
|
||||
String displayName = FileUtil.mainName(fileName);
|
||||
|
||||
TikUserFileDO userFile = new TikUserFileDO()
|
||||
@@ -224,13 +199,11 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
.setFileId(infraFileId)
|
||||
.setFileName(fileName)
|
||||
.setDisplayName(displayName)
|
||||
.setFileType(fileType)
|
||||
.setFileType(file.getContentType())
|
||||
.setFileCategory(fileCategory)
|
||||
.setFileSize(file.getSize())
|
||||
.setFileUrl(fileUrl)
|
||||
.setFilePath(filePath)
|
||||
.setCoverUrl(coverUrl)
|
||||
.setCoverBase64(StrUtil.isNotBlank(coverBase64) ? coverBase64 : null)
|
||||
.setDuration(duration)
|
||||
.setGroupId(groupId);
|
||||
|
||||
@@ -278,11 +251,19 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
vo.setIsVideo(isVideo);
|
||||
vo.setIsImage(isImage);
|
||||
|
||||
// 视频文件不生成OSS预签名URL(前端使用coverBase64缓存)
|
||||
// 视频文件:使用 OSS 截帧
|
||||
if (isVideo) {
|
||||
vo.setCoverUrl(null);
|
||||
vo.setThumbnailUrl(null);
|
||||
vo.setPreviewUrl(null);
|
||||
// 使用 OSS 视频截帧作为封面(预签名时包含截帧参数,避免 403)
|
||||
// t_1000: 截取1秒处(避免开头黑屏/非关键帧)
|
||||
// f_jpg: 输出JPG格式
|
||||
// w_300: 宽度300px
|
||||
if (StrUtil.isNotBlank(file.getFileUrl())) {
|
||||
String snapshotUrl = getCachedPresignUrlWithProcess(
|
||||
file.getFileUrl(),
|
||||
PRESIGN_URL_EXPIRATION_SECONDS,
|
||||
"video/snapshot,t_1000,f_jpg,w_300");
|
||||
vo.setPreviewUrl(snapshotUrl);
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -290,7 +271,6 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
String thumbnailUrl = StrUtil.isNotBlank(file.getThumbnailUrl())
|
||||
? getCachedPresignUrl(file.getThumbnailUrl(), PRESIGN_URL_EXPIRATION_SECONDS)
|
||||
: null;
|
||||
vo.setCoverUrl(null);
|
||||
vo.setThumbnailUrl(thumbnailUrl);
|
||||
|
||||
// 图片预览URL:优先缩略图,否则原图
|
||||
@@ -381,15 +361,12 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
* 确定预览URL
|
||||
*/
|
||||
private String determinePreviewUrl(TikUserFileDO file, String type) {
|
||||
// 视频文件:不返回任何OSS URL(前端使用coverBase64缓存)
|
||||
// 视频文件:使用 OSS 截帧,通过 getFilePage 返回 previewUrl
|
||||
if (StrUtil.containsIgnoreCase(file.getFileType(), "video")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 明确指定类型
|
||||
if ("cover".equals(type) && StrUtil.isNotBlank(file.getCoverUrl())) {
|
||||
return file.getCoverUrl();
|
||||
}
|
||||
if ("thumbnail".equals(type) && StrUtil.isNotBlank(file.getThumbnailUrl())) {
|
||||
return file.getThumbnailUrl();
|
||||
}
|
||||
@@ -402,7 +379,7 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "tik:file:presign",
|
||||
@Cacheable(value = "tik:file:presign#23h",
|
||||
key = "#url + ':' + (#expirationSeconds != null ? #expirationSeconds : 86400)")
|
||||
public String getCachedPresignUrl(String url, Integer expirationSeconds) {
|
||||
if (StrUtil.isBlank(url)) {
|
||||
@@ -411,6 +388,18 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
return fileApi.presignGetUrl(url, expirationSeconds != null ? expirationSeconds : 86400);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "tik:file:presign:process#23h",
|
||||
key = "#url + ':' + (#expirationSeconds != null ? #expirationSeconds : 86400) + ':' + #processParam")
|
||||
public String getCachedPresignUrlWithProcess(String url, Integer expirationSeconds, String processParam) {
|
||||
if (StrUtil.isBlank(url)) {
|
||||
return null;
|
||||
}
|
||||
return fileApi.presignGetUrlWithProcess(url,
|
||||
expirationSeconds != null ? expirationSeconds : 86400,
|
||||
processParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPresignedUrl(String fileName, String fileCategory, Long groupId, Long fileSize, String contentType) {
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
@@ -490,7 +479,6 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
Long fileSize = params.get("fileSize") != null ? ((Number) params.get("fileSize")).longValue() : 0L;
|
||||
String fileType = (String) params.get("fileType");
|
||||
Long groupId = params.get("groupId") != null ? ((Number) params.get("groupId")).longValue() : null;
|
||||
String coverBase64 = (String) params.get("coverBase64");
|
||||
Integer duration = params.get("duration") != null ? ((Number) params.get("duration")).intValue() : null;
|
||||
|
||||
validateUploadRequest(fileCategory, fileSize);
|
||||
@@ -509,8 +497,6 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
fileMapper.insert(infraFile);
|
||||
Long infraFileId = infraFile.getId();
|
||||
|
||||
String baseDirectory = ossInitService.getOssDirectoryByCategory(userId, fileCategory);
|
||||
String coverUrl = handleCoverUpload(coverBase64, fileName, fileType, baseDirectory);
|
||||
String displayName = FileUtil.mainName(fileName);
|
||||
|
||||
TikUserFileDO userFile = new TikUserFileDO()
|
||||
@@ -523,8 +509,6 @@ public class TikUserFileServiceImpl implements TikUserFileService {
|
||||
.setFileSize(fileSize)
|
||||
.setFileUrl(fileUrl)
|
||||
.setFilePath(fileKey)
|
||||
.setCoverUrl(coverUrl)
|
||||
.setCoverBase64(coverBase64)
|
||||
.setDuration(duration)
|
||||
.setGroupId(groupId);
|
||||
userFileMapper.insert(userFile);
|
||||
|
||||
@@ -47,12 +47,6 @@ public class AppTikUserFileRespVO {
|
||||
@Schema(description = "是否为图片文件", example = "false")
|
||||
private Boolean isImage;
|
||||
|
||||
@Schema(description = "封面图URL(视频文件的封面图)")
|
||||
private String coverUrl;
|
||||
|
||||
@Schema(description = "封面图Base64(视频文件的封面图原始base64数据,可选)")
|
||||
private String coverBase64;
|
||||
|
||||
@Schema(description = "缩略图URL(图片文件的缩略图)")
|
||||
private String thumbnailUrl;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user