diff --git a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/EpisodeTranscodeBindingService.java b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/EpisodeTranscodeBindingService.java index 89c740e..831a7f8 100644 --- a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/EpisodeTranscodeBindingService.java +++ b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/EpisodeTranscodeBindingService.java @@ -19,7 +19,7 @@ import org.springframework.util.StringUtils; public class EpisodeTranscodeBindingService { private final MediaAssetMapper mediaAssetMapper; - private final MediaStorageService storageService; + private final MediaStorage storageService; private final TranscodeService transcodeService; /** diff --git a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaService.java b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaService.java index b1e7dd7..bbb7f1b 100644 --- a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaService.java +++ b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaService.java @@ -38,7 +38,7 @@ public class MediaService { Set.of(".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".avif"); private final MediaProperties properties; - private final MediaStorageService storageService; + private final MediaStorage storageService; private final MediaAssetMapper mediaAssetMapper; private final MediaUploadTaskMapper mediaUploadTaskMapper; private final TranscodeService transcodeService; @@ -104,7 +104,7 @@ public class MediaService { "分片未传完(" + chunks + "/" + task.getTotalChunks() + ")"); } - MediaStorageService.MergeResult merged = + MediaStorage.MergeResult merged = storageService.merge(req.getUploadId(), task.getObjectKey(), task.getTotalChunks()); String publicUrl = storageService.toPublicUrl(task.getObjectKey()); diff --git a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorage.java b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorage.java new file mode 100644 index 0000000..20d6961 --- /dev/null +++ b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorage.java @@ -0,0 +1,58 @@ +// Author: fruit.com.cn + +package com.fruitshorts.media.service; + +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Path; + +/** + * 媒资存储抽象:当前实现为本地磁盘({@link MediaStorageService}), + * 后期新增 S3 实现后以配置切换,业务代码不感知。 + * + *
S3 实现要点:分片走 multipart upload 或本地暂存后 putObject;
+ * {@link #fileFor(String)} 改成「下载到临时目录 / 上传回对象存储」的适配(ffmpeg 需要本地文件);
+ * {@link #toPublicUrl(String)} 返回 CDN / 对象存储域名地址。
+ */
+public interface MediaStorage {
+
+ /** 合并结果:落地路径、大小与 MD5。 */
+ record MergeResult(Path path, long size, String md5) {
+ }
+
+ /** 存储根目录(本地实现为磁盘目录;S3 实现为本地暂存目录)。 */
+ Path rootDir();
+
+ /**
+ * 把 objectKey 解析成本地可读写的路径。转码产物落地用它。
+ * 实现方必须校验 objectKey 不允许跑出根目录。
+ */
+ Path fileFor(String objectKey);
+
+ /** 把对外 URL 反解成 objectKey(去掉前缀与签名查询串)。 */
+ String objectKeyOf(String publicUrl);
+
+ /** 分片临时目录。 */
+ Path chunkDir(String uploadId);
+
+ /** 保存一个分片。 */
+ void saveChunk(String uploadId, int index, MultipartFile file);
+
+ /** 已收到的分片数。 */
+ int countChunks(String uploadId);
+
+ /** 按分片序号顺序合并成正式文件,返回大小与 MD5。 */
+ MergeResult merge(String uploadId, String objectKey, int totalChunks);
+
+ /** 清理分片临时目录。 */
+ void cleanChunkDir(String uploadId);
+
+ /** 删除一个媒资文件。 */
+ void delete(String objectKey);
+
+ /** 生成对象键:bizType/yyyyMM/uuid.ext。 */
+ String buildObjectKey(String bizType, String originalName);
+
+ /** objectKey 转对外访问 URL。 */
+ String toPublicUrl(String objectKey);
+}
diff --git a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorageService.java b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorageService.java
index 55ccb94..1ca2a66 100644
--- a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorageService.java
+++ b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaStorageService.java
@@ -28,20 +28,17 @@ import java.util.UUID;
/**
* 本地磁盘媒资存储:分片写临时目录,完成后合并成正式文件。
- * 之后要换成对象存储时,只需替换本类实现。
+ * 是 {@link MediaStorage} 的本地实现;切对象存储时新增实现类即可,业务代码只依赖接口。
*/
@Slf4j
@Service
@RequiredArgsConstructor
-public class MediaStorageService {
+public class MediaStorageService implements MediaStorage {
private static final DateTimeFormatter MONTH_DIR = DateTimeFormatter.ofPattern("yyyyMM");
private final MediaProperties properties;
- public record MergeResult(Path path, long size, String md5) {
- }
-
public Path rootDir() {
Path root = Paths.get(properties.getRootDir()).toAbsolutePath().normalize();
try {
diff --git a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaTranscodeOutputService.java b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaTranscodeOutputService.java
index 9385963..774afe2 100644
--- a/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaTranscodeOutputService.java
+++ b/fruitshorts-module-media/src/main/java/com/fruitshorts/media/service/MediaTranscodeOutputService.java
@@ -50,7 +50,7 @@ public class MediaTranscodeOutputService {
private final MediaAssetMapper mediaAssetMapper;
private final TranscodeService transcodeService;
- private final MediaStorageService storageService;
+ private final MediaStorage storageService;
private final PlaybackSigner signer;
public List