feat: 支持视频及缩略图元数据存储
All checks were successful
test/timeline-server/pipeline/head This commit looks good
All checks were successful
test/timeline-server/pipeline/head This commit looks good
在文件服务和故事服务中增加了对视频、持续时间及缩略图相关字段的支持。 - 在 `ImageInfo` 和 `StoryItem` 实体类中添加 `duration`、`thumbnailInstanceId` 等字段 - 更新 MyBatis 映射文件以支持新字段的持久化 - 在 `FileService` 中新增 `generateVideoUrl` 接口用于获取视频预签名地址 - 调整 `saveFileMetadata` 接口返回生成的 `instanceId` - 优化了部分代码的格式和缩进
This commit is contained in:
@@ -24,10 +24,12 @@ import java.util.Map;
|
||||
public class FileController {
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@GetMapping("/hello")
|
||||
public String hello(){
|
||||
public String hello() {
|
||||
return "file service hello";
|
||||
}
|
||||
|
||||
@GetMapping("/create-default-bucket")
|
||||
public String createDefaultBucket() throws Throwable {
|
||||
fileService.createBucketIfNotExist();
|
||||
@@ -46,21 +48,31 @@ public class FileController {
|
||||
fileService.createUserBucket(userId);
|
||||
return ResponseEntity.success("bucket created for current user");
|
||||
}
|
||||
|
||||
@GetMapping("/get-upload-url/{fileName}")
|
||||
public ResponseEntity<String> getUploadUrl(@PathVariable String fileName) throws Throwable {
|
||||
String uploadUrl = fileService.generateUploadUrl(fileName);
|
||||
return ResponseEntity.success(uploadUrl);
|
||||
}
|
||||
|
||||
@GetMapping("/get-download-url/{fileName}")
|
||||
public ResponseEntity<String> getDownloadUrl(@PathVariable String fileName) throws Throwable {
|
||||
String downloadUrl = fileService.generateDownloadUrl(fileName);
|
||||
return ResponseEntity.success(downloadUrl);
|
||||
}
|
||||
|
||||
@GetMapping("/get-video-url/{instanceId}")
|
||||
public ResponseEntity<String> getVideoUrl(@PathVariable String instanceId) throws Throwable {
|
||||
String videoUrl = fileService.generateVideoUrl(instanceId);
|
||||
return ResponseEntity.success(videoUrl);
|
||||
}
|
||||
|
||||
@PostMapping("/uploaded")
|
||||
public ResponseEntity<String> uploaded(@RequestBody ImageInfoVo imageInfoVo) throws Throwable {
|
||||
fileService.saveFileMetadata(imageInfoVo);
|
||||
return ResponseEntity.success("上传成功");
|
||||
String instanceId = fileService.saveFileMetadata(imageInfoVo);
|
||||
return ResponseEntity.success(instanceId);
|
||||
}
|
||||
|
||||
@PostMapping("/upload-image")
|
||||
public ResponseEntity<String> uploadCover(@RequestPart("image") MultipartFile image) throws Throwable {
|
||||
String objectKey = fileService.uploadImage(image);
|
||||
@@ -73,12 +85,14 @@ public class FileController {
|
||||
response.setContentType("image/jpeg");
|
||||
IOUtils.copy(inputStream, response.getOutputStream());
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/image-low-res/{instanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
|
||||
public void fetchImageLowRes(@PathVariable String instanceId, HttpServletResponse response) throws Throwable {
|
||||
InputStream inputStream = fileService.fetchImageLowRes(instanceId);
|
||||
response.setContentType("image/jpeg");
|
||||
IOUtils.copy(inputStream, response.getOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片后绑定到某个 StoryItem
|
||||
*/
|
||||
@@ -119,12 +133,15 @@ public class FileController {
|
||||
fileService.removeImageFromStoryItem(imageInstanceId, storyItemId);
|
||||
return ResponseEntity.success("图片已从故事项中移除");
|
||||
}
|
||||
|
||||
@GetMapping("/image/list")
|
||||
public ResponseEntity<Map<String, Object>> getImagesListByOwnerId(@SpringQueryMap ImageInfoVo imageInfoVo) throws Throwable {
|
||||
public ResponseEntity<Map<String, Object>> getImagesListByOwnerId(@SpringQueryMap ImageInfoVo imageInfoVo)
|
||||
throws Throwable {
|
||||
imageInfoVo.setOwnerId(UserContextUtils.getCurrentUserId());
|
||||
Map<String, Object> images = fileService.getImagesListByOwnerId(imageInfoVo);
|
||||
return ResponseEntity.success(images);
|
||||
}
|
||||
|
||||
@DeleteMapping("/image/{imageInstanceId}")
|
||||
public ResponseEntity<String> deleteImage(@PathVariable String imageInstanceId) throws Throwable {
|
||||
fileService.deleteImage(imageInstanceId);
|
||||
|
||||
@@ -16,4 +16,6 @@ public class ImageInfo {
|
||||
private String userId;
|
||||
private Integer isDeleted;
|
||||
private LocalDateTime updateTime;
|
||||
private String thumbnailInstanceId;
|
||||
private Long duration;
|
||||
}
|
||||
|
||||
@@ -13,20 +13,34 @@ import java.util.Map;
|
||||
@Service
|
||||
public interface FileService {
|
||||
void createBucketIfNotExist() throws Throwable;
|
||||
|
||||
void createUserBucket(String userId) throws Throwable;
|
||||
|
||||
String generateUploadUrl(String fileName) throws Throwable;
|
||||
|
||||
String generateDownloadUrl(String fileName) throws Throwable;
|
||||
void saveFileMetadata(ImageInfoVo imageInfoVo);
|
||||
|
||||
String saveFileMetadata(ImageInfoVo imageInfoVo);
|
||||
|
||||
List<ImageInfo> listAllImages();
|
||||
|
||||
void deleteImage(String objectKey) throws Throwable;
|
||||
|
||||
void associateImageWithStoryItem(String imageInstanceId, String storyItemId, String userId);
|
||||
|
||||
List<String> getStoryItemImages(String storyItemId);
|
||||
|
||||
void removeImageFromStoryItem(String imageInstanceId, String storyItemId);
|
||||
|
||||
ArrayList<String> getAllImageUrls(List<String> images) throws Throwable;
|
||||
|
||||
String uploadImage(MultipartFile cover) throws Throwable;
|
||||
|
||||
InputStream fetchImage(String coverKey) throws Throwable;
|
||||
|
||||
InputStream fetchImageLowRes(String instanceId) throws Throwable;
|
||||
|
||||
String generateVideoUrl(String instanceId) throws Throwable;
|
||||
|
||||
Map<String, Object> getImagesListByOwnerId(ImageInfoVo imageInfoVo);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class FileServiceImpl implements FileService {
|
||||
private CommonRelationMapper commonRelationMapper;
|
||||
@Autowired
|
||||
private FileHashMapper fileHashMapper;
|
||||
|
||||
public FileServiceImpl(MinioClient minioClient, MinioConfig minioConfig) {
|
||||
this.minioClient = minioClient;
|
||||
this.minioConfig = minioConfig;
|
||||
@@ -62,7 +63,8 @@ public class FileServiceImpl implements FileService {
|
||||
@Override
|
||||
public void createBucketIfNotExist() throws Throwable {
|
||||
try {
|
||||
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build());
|
||||
boolean found = minioClient
|
||||
.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build());
|
||||
if (!found) {
|
||||
log.info("bucket不存在,创建bucket:{}", minioConfig.getBucketName());
|
||||
minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucketName()).build());
|
||||
@@ -97,8 +99,7 @@ public class FileServiceImpl implements FileService {
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.PUT)
|
||||
.bucket(bucket)
|
||||
.object(fileName).build()
|
||||
);
|
||||
.object(fileName).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,22 +110,42 @@ public class FileServiceImpl implements FileService {
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(bucket)
|
||||
.object(fileName).build()
|
||||
);
|
||||
.object(fileName).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveFileMetadata(ImageInfoVo imageInfoVo) {
|
||||
public String generateVideoUrl(String instanceId) throws Throwable {
|
||||
ImageInfo imageInfo = imageInfoMapper.selectByInstanceId(instanceId);
|
||||
if (imageInfo == null) {
|
||||
throw new CustomException(404, "视频文件不存在");
|
||||
}
|
||||
String bucket = userBucket(imageInfo.getUserId());
|
||||
// 生成预签名 URL,有效期例如 1 小时
|
||||
return minioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(bucket)
|
||||
.object(imageInfo.getObjectKey())
|
||||
.expiry(3600) // 1小时
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveFileMetadata(ImageInfoVo imageInfoVo) {
|
||||
try {
|
||||
ImageInfo imageInfo = new ImageInfo();
|
||||
imageInfo.setInstanceId(IdUtils.randomUuidUpper());
|
||||
String instanceId = IdUtils.randomUuidUpper();
|
||||
imageInfo.setInstanceId(instanceId);
|
||||
imageInfo.setObjectKey(imageInfoVo.getObjectKey());
|
||||
imageInfo.setImageName(imageInfoVo.getImageName());
|
||||
imageInfo.setContentType(imageInfoVo.getContentType());
|
||||
imageInfo.setSize(imageInfoVo.getSize());
|
||||
imageInfo.setUploadTime(LocalDateTime.now());
|
||||
imageInfo.setUserId(currentUserId());
|
||||
imageInfo.setThumbnailInstanceId(imageInfoVo.getThumbnailInstanceId());
|
||||
imageInfo.setDuration(imageInfoVo.getDuration());
|
||||
imageInfoMapper.insert(imageInfo);
|
||||
return instanceId;
|
||||
} catch (Exception e) {
|
||||
log.error("保存图片元数据失败", e);
|
||||
throw new CustomException(500, "保存图片信息失败");
|
||||
@@ -174,6 +195,7 @@ public class FileServiceImpl implements FileService {
|
||||
throw new CustomException(500, "删除图片失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void associateImageWithStoryItem(String imageInstanceId, String storyItemId, String userId) {
|
||||
try {
|
||||
@@ -200,7 +222,7 @@ public class FileServiceImpl implements FileService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getAllImageUrls(List<String > imageIds) throws Throwable {
|
||||
public ArrayList<String> getAllImageUrls(List<String> imageIds) throws Throwable {
|
||||
ArrayList<String> urls = new ArrayList<>();
|
||||
|
||||
for (String imageInstanceId : imageIds) {
|
||||
@@ -214,8 +236,7 @@ public class FileServiceImpl implements FileService {
|
||||
.method(Method.GET)
|
||||
.bucket(bucket)
|
||||
.object(info.getObjectKey())
|
||||
.build()
|
||||
);
|
||||
.build());
|
||||
urls.add(url);
|
||||
}
|
||||
return urls;
|
||||
@@ -223,7 +244,8 @@ public class FileServiceImpl implements FileService {
|
||||
|
||||
@Override
|
||||
public String uploadImage(MultipartFile image) throws Throwable {
|
||||
String suffix = Objects.requireNonNull(image.getOriginalFilename()).substring(image.getOriginalFilename().lastIndexOf("."));
|
||||
String suffix = Objects.requireNonNull(image.getOriginalFilename())
|
||||
.substring(image.getOriginalFilename().lastIndexOf("."));
|
||||
String hash = CommonUtils.calculateFileHash(image);
|
||||
String objectKey = hash + suffix;
|
||||
String lowResolutionObjectKey = CommonConstants.LOW_RESOLUTION_PREFIX + hash + suffix;
|
||||
@@ -300,6 +322,7 @@ public class FileServiceImpl implements FileService {
|
||||
.object(objectKey)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream fetchImageLowRes(String instanceId) throws Throwable {
|
||||
String objectKey = imageInfoMapper.selectObjectKeyById(instanceId);
|
||||
@@ -325,18 +348,21 @@ public class FileServiceImpl implements FileService {
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getImagesListByOwnerId(ImageInfoVo imageInfoVo) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("ownerId", imageInfoVo.getOwnerId());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> resultMap = (Map<String, Object>) PageUtils.pageQuery(imageInfoVo.getCurrent(), imageInfoVo.getPageSize(), ImageInfoMapper.class, "selectListByOwnerId",
|
||||
Map<String, Object> resultMap = (Map<String, Object>) PageUtils.pageQuery(imageInfoVo.getCurrent(),
|
||||
imageInfoVo.getPageSize(), ImageInfoMapper.class, "selectListByOwnerId",
|
||||
map, "list");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否存在
|
||||
*
|
||||
* @param objectKey 对象键
|
||||
* @return true表示存在,false表示不存在
|
||||
*/
|
||||
|
||||
@@ -11,4 +11,6 @@ public class ImageInfoVo extends CommonVo {
|
||||
private Long size;
|
||||
private String instanceId;
|
||||
private String ownerId;
|
||||
private String thumbnailInstanceId;
|
||||
private Long duration;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user