From d645164daaa1fa1d08e49e711d37a44c4b10c485 Mon Sep 17 00:00:00 2001 From: jianghao <332515344@qq.com> Date: Fri, 13 Feb 2026 11:14:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=B5=81=E5=BC=8F=E6=92=AD=E6=94=BE=E4=B8=8E=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=9F=A5=E8=AF=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在 `FileController` 中新增 `/video/{instanceId}` 接口,支持 HTTP Range 请求以实现视频分段加载和流式播放。 2. 在 `FileService` 和 `ImageInfoMapper` 中新增 `getBatchFileInfo` 方法,支持通过实例 ID 列表批量获取文件元数据。 3. 优化 `getVideoUrl` 逻辑,改为返回服务内部代理路径而非 MinIO 直接签名地址。 4. 完善相关 DAO 层代码,增加 `selectListByInstanceIds` 查询语句。 --- .../file/controller/FileController.java | 51 +++++++++++++++++- .../timeline/file/dao/ImageInfoMapper.java | 8 +++ .../timeline/file/service/FileService.java | 8 ++- .../file/service/impl/FileServiceImpl.java | 52 ++++++++++++++++++- .../com/timeline/file/dao/ImageInfoMapper.xml | 7 +++ 5 files changed, 123 insertions(+), 3 deletions(-) diff --git a/timeline-file-service/src/main/java/com/timeline/file/controller/FileController.java b/timeline-file-service/src/main/java/com/timeline/file/controller/FileController.java index 881b14c..b1d24bb 100644 --- a/timeline-file-service/src/main/java/com/timeline/file/controller/FileController.java +++ b/timeline-file-service/src/main/java/com/timeline/file/controller/FileController.java @@ -63,10 +63,53 @@ public class FileController { @GetMapping("/get-video-url/{instanceId}") public ResponseEntity getVideoUrl(@PathVariable String instanceId) throws Throwable { - String videoUrl = fileService.generateVideoUrl(instanceId); + // Return proxy URL instead of direct MinIO URL + // Assuming the frontend can access this service via /api/file + String videoUrl = "/api/file/video/" + instanceId; return ResponseEntity.success(videoUrl); } + @GetMapping("/video/{instanceId}") + public void fetchVideo(@PathVariable String instanceId, + @RequestHeader(value = "Range", required = false) String rangeHeader, + HttpServletResponse response) throws Throwable { + long fileSize = fileService.getVideoSize(instanceId); + long start = 0; + long end = fileSize - 1; + + if (rangeHeader != null && rangeHeader.startsWith("bytes=")) { + String[] ranges = rangeHeader.substring(6).split("-"); + try { + start = Long.parseLong(ranges[0]); + if (ranges.length > 1 && !ranges[1].isEmpty()) { + end = Long.parseLong(ranges[1]); + } + } catch (NumberFormatException e) { + // Ignore invalid range + } + } + + if (end >= fileSize) { + end = fileSize - 1; + } + + long length = end - start + 1; + + response.setContentType("video/mp4"); + response.setHeader("Accept-Ranges", "bytes"); + + if (rangeHeader != null) { + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); + response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize); + response.setHeader("Content-Length", String.valueOf(length)); + } else { + response.setHeader("Content-Length", String.valueOf(fileSize)); + } + + InputStream inputStream = fileService.fetchVideo(instanceId, start, length); + IOUtils.copy(inputStream, response.getOutputStream()); + } + @PostMapping("/uploaded") public ResponseEntity uploaded(@RequestBody ImageInfoVo imageInfoVo) throws Throwable { String instanceId = fileService.saveFileMetadata(imageInfoVo); @@ -79,6 +122,12 @@ public class FileController { return ResponseEntity.success(objectKey); } + @PostMapping("/batch-info") + public ResponseEntity> getBatchFileInfo(@RequestBody List instanceIds) { + List list = fileService.getBatchFileInfo(instanceIds); + return ResponseEntity.success(list); + } + @RequestMapping(value = "/image/{instanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE) public void fetchImage(@PathVariable String instanceId, HttpServletResponse response) throws Throwable { InputStream inputStream = fileService.fetchImage(instanceId); diff --git a/timeline-file-service/src/main/java/com/timeline/file/dao/ImageInfoMapper.java b/timeline-file-service/src/main/java/com/timeline/file/dao/ImageInfoMapper.java index 2b08722..921eae3 100644 --- a/timeline-file-service/src/main/java/com/timeline/file/dao/ImageInfoMapper.java +++ b/timeline-file-service/src/main/java/com/timeline/file/dao/ImageInfoMapper.java @@ -3,6 +3,7 @@ package com.timeline.file.dao; import com.timeline.file.entity.ImageInfo; import com.timeline.file.vo.ImageInfoVo; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; @@ -10,9 +11,16 @@ import java.util.Map; @Mapper public interface ImageInfoMapper { void insert(ImageInfo imageInfo); + void update(ImageInfo imageInfo); + + List selectListByInstanceIds(@Param("ids") List ids); + String selectObjectKeyById(String objectKey); + void delete(String objectKey); + List selectListByOwnerId(Map map); + ImageInfo selectByInstanceId(String instanceId); } diff --git a/timeline-file-service/src/main/java/com/timeline/file/service/FileService.java b/timeline-file-service/src/main/java/com/timeline/file/service/FileService.java index 77acb3e..98bfb70 100644 --- a/timeline-file-service/src/main/java/com/timeline/file/service/FileService.java +++ b/timeline-file-service/src/main/java/com/timeline/file/service/FileService.java @@ -36,11 +36,17 @@ public interface FileService { String uploadImage(MultipartFile cover) throws Throwable; - InputStream fetchImage(String coverKey) throws Throwable; + InputStream fetchImage(String instanceId) throws Throwable; InputStream fetchImageLowRes(String instanceId) throws Throwable; + InputStream fetchVideo(String instanceId, long offset, long length) throws Throwable; + + long getVideoSize(String instanceId); + String generateVideoUrl(String instanceId) throws Throwable; Map getImagesListByOwnerId(ImageInfoVo imageInfoVo); + + List getBatchFileInfo(List instanceIds); } diff --git a/timeline-file-service/src/main/java/com/timeline/file/service/impl/FileServiceImpl.java b/timeline-file-service/src/main/java/com/timeline/file/service/impl/FileServiceImpl.java index bfc60af..a6b7820 100644 --- a/timeline-file-service/src/main/java/com/timeline/file/service/impl/FileServiceImpl.java +++ b/timeline-file-service/src/main/java/com/timeline/file/service/impl/FileServiceImpl.java @@ -28,7 +28,13 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.time.LocalDateTime; -import java.util.*; +import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; @Slf4j @Service @@ -386,6 +392,50 @@ public class FileServiceImpl implements FileService { } } + @Override + public InputStream fetchVideo(String instanceId, long offset, long length) throws Throwable { + ImageInfo imageInfo = imageInfoMapper.selectByInstanceId(instanceId); + if (imageInfo == null) { + throw new CustomException(ResponseEnum.NOT_FOUND_ERROR); + } + String bucket = userBucket(imageInfo.getUserId()); + return minioClient.getObject(GetObjectArgs.builder() + .bucket(bucket) + .object(imageInfo.getObjectKey()) + .offset(offset) + .length(length) + .build()); + } + + @Override + public long getVideoSize(String instanceId) { + ImageInfo imageInfo = imageInfoMapper.selectByInstanceId(instanceId); + if (imageInfo == null) { + throw new CustomException(ResponseEnum.NOT_FOUND_ERROR); + } + return imageInfo.getSize(); + } + + @Override + public List getBatchFileInfo(List instanceIds) { + if (instanceIds == null || instanceIds.isEmpty()) { + return new ArrayList<>(); + } + List imageInfos = imageInfoMapper.selectListByInstanceIds(instanceIds); + if (imageInfos == null) { + return new ArrayList<>(); + } + return imageInfos.stream().map(info -> { + ImageInfoVo vo = new ImageInfoVo(); + vo.setInstanceId(info.getInstanceId()); + vo.setImageName(info.getImageName()); + vo.setThumbnailInstanceId(info.getThumbnailInstanceId()); + vo.setContentType(info.getContentType()); + vo.setDuration(info.getDuration()); + return vo; + }).collect(Collectors.toList()); + } + @Override public Map getImagesListByOwnerId(ImageInfoVo imageInfoVo) { HashMap map = new HashMap<>(); diff --git a/timeline-file-service/src/main/resources/com/timeline/file/dao/ImageInfoMapper.xml b/timeline-file-service/src/main/resources/com/timeline/file/dao/ImageInfoMapper.xml index 8ca16cc..1c1764c 100644 --- a/timeline-file-service/src/main/resources/com/timeline/file/dao/ImageInfoMapper.xml +++ b/timeline-file-service/src/main/resources/com/timeline/file/dao/ImageInfoMapper.xml @@ -43,4 +43,11 @@ is_deleted = #{isDeleted} WHERE instance_id = #{instanceId} +