From 1b1e1f4f87b52d6992e69e2dece98a4e167de84c Mon Sep 17 00:00:00 2001 From: jianghao <332515344@qq.com> Date: Thu, 12 Feb 2026 16:54:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E7=AB=AF=E7=82=B9=E6=98=A0=E5=B0=84=E4=B8=8E=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E5=85=83=E6=95=B0=E6=8D=AE=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在 MinioConfig 中增加 externalEndpoint 配置,支持将生成的预签名 URL 内部地址替换为外部访问地址。 2. 更新数据库脚本及查询逻辑,增加视频时长、缩略图 ID 等字段支持,并在 查询列表时过滤掉作为缩略图存在的冗余记录。 3. 优化图片上传流程,增加压缩失败时的降级处理机制,防止非图片文件导致 上传中断。 --- deploy/update_image_info.sql | 8 ++ .../com/timeline/file/config/MinioConfig.java | 1 + .../file/service/impl/FileServiceImpl.java | 73 ++++++++++++++----- .../com/timeline/file/dao/ImageInfoMapper.xml | 9 ++- 4 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 deploy/update_image_info.sql diff --git a/deploy/update_image_info.sql b/deploy/update_image_info.sql new file mode 100644 index 0000000..77319ce --- /dev/null +++ b/deploy/update_image_info.sql @@ -0,0 +1,8 @@ +ALTER TABLE `image_info` +ADD COLUMN `thumbnail_instance_id` varchar(32) DEFAULT NULL COMMENT '视频缩略图ID', +ADD COLUMN `duration` bigint DEFAULT NULL COMMENT '视频时长(秒)';ALTER TABLE `story_item` +ADD COLUMN `video_url` VARCHAR(64) COMMENT '视频文件 Instance ID', +ADD COLUMN `duration` BIGINT COMMENT '视频时长(秒)', +ADD COLUMN `thumbnail_url` VARCHAR(64) COMMENT '视频封面 Instance ID';ALTALTER TABLE story_item ADD COLUMN share_id VARCHAR(64) DEFAULT NULL COMMENT '分享ID';ER TABLE `image_info` +ADD COLUMN `thumbnail_instance_id` varchar(32) DEFAULT NULL COMMENT '视频缩略图ID', +ADD COLUMN `duration` bigint DEFAULT NULL COMMENT '视频时长(秒)'; diff --git a/timeline-file-service/src/main/java/com/timeline/file/config/MinioConfig.java b/timeline-file-service/src/main/java/com/timeline/file/config/MinioConfig.java index 5c9b650..58cbde5 100644 --- a/timeline-file-service/src/main/java/com/timeline/file/config/MinioConfig.java +++ b/timeline-file-service/src/main/java/com/timeline/file/config/MinioConfig.java @@ -11,6 +11,7 @@ import org.springframework.context.annotation.Configuration; @ConfigurationProperties(prefix = "minio") public class MinioConfig { private String endpoint; + private String externalEndpoint; private String accessKey; private String secretKey; private String bucketName; 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 3ec8972..bfc60af 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 @@ -90,27 +90,44 @@ public class FileServiceImpl implements FileService { } } + private String replaceWithExternalEndpoint(String url) { + String externalEndpoint = minioConfig.getExternalEndpoint(); + if (externalEndpoint != null && !externalEndpoint.isEmpty()) { + String internalEndpoint = minioConfig.getEndpoint(); + // 简单替换:将内部 Endpoint 替换为外部 Endpoint + // 注意:MinIO 生成的 URL 肯定以配置的 Endpoint 开头 + if (url.startsWith(internalEndpoint)) { + return url.replaceFirst(java.util.regex.Pattern.quote(internalEndpoint), externalEndpoint); + } + } + return url; + } + @Override public String generateUploadUrl(String fileName) throws Throwable { String userId = currentUserId(); String bucket = userBucket(userId); createUserBucket(userId); - return minioClient.getPresignedObjectUrl( + String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .method(Method.PUT) .bucket(bucket) - .object(fileName).build()); + .object(fileName) + .expiry(30 * 60) // 30 minutes + .build()); + return replaceWithExternalEndpoint(url); } @Override public String generateDownloadUrl(String fileName) throws Throwable { String userId = currentUserId(); String bucket = userBucket(userId); - return minioClient.getPresignedObjectUrl( + String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucket) .object(fileName).build()); + return replaceWithExternalEndpoint(url); } @Override @@ -121,13 +138,14 @@ public class FileServiceImpl implements FileService { } String bucket = userBucket(imageInfo.getUserId()); // 生成预签名 URL,有效期例如 1 小时 - return minioClient.getPresignedObjectUrl( + String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucket) .object(imageInfo.getObjectKey()) .expiry(3600) // 1小时 .build()); + return replaceWithExternalEndpoint(url); } @Override @@ -237,7 +255,7 @@ public class FileServiceImpl implements FileService { .bucket(bucket) .object(info.getObjectKey()) .build()); - urls.add(url); + urls.add(replaceWithExternalEndpoint(url)); } return urls; } @@ -267,20 +285,39 @@ public class FileServiceImpl implements FileService { log.info("当前文件已存在,不进行minio文件上传"); } else { // 1. 上传到 MinIO - // 对原图进行压缩 + // 尝试对原图进行压缩 ByteArrayOutputStream compressedOutputStream = new ByteArrayOutputStream(); - Thumbnails.of(image.getInputStream()) - .scale(1.0) // 保持原图尺寸 - .outputQuality(0.8) // 设置压缩质量 - .toOutputStream(compressedOutputStream); - ByteArrayInputStream compressedInputStream = new ByteArrayInputStream(compressedOutputStream.toByteArray()); + boolean compressionSuccess = false; + try { + Thumbnails.of(image.getInputStream()) + .scale(1.0) // 保持原图尺寸 + .outputQuality(0.8) // 设置压缩质量 + .toOutputStream(compressedOutputStream); + compressionSuccess = true; + } catch (Exception e) { + log.warn("图片压缩失败(可能是格式不支持或非图片文件),降级为直接上传原图: {}", image.getOriginalFilename(), e); + } - minioClient.putObject(PutObjectArgs.builder() - .bucket(bucket) - .object(objectKey) - .stream(compressedInputStream, compressedInputStream.available(), -1) - .contentType(image.getContentType()) - .build()); + if (compressionSuccess) { + ByteArrayInputStream compressedInputStream = new ByteArrayInputStream( + compressedOutputStream.toByteArray()); + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucket) + .object(objectKey) + .stream(compressedInputStream, compressedInputStream.available(), -1) + .contentType(image.getContentType()) + .build()); + } else { + // 压缩失败,直接上传原图 + try (InputStream inputStream = image.getInputStream()) { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucket) + .object(objectKey) + .stream(inputStream, image.getSize(), -1) + .contentType(image.getContentType()) + .build()); + } + } // 生成并上传低分辨率版本 try (InputStream inputStream = image.getInputStream()) { ByteArrayOutputStream lowResOutputStream = new ByteArrayOutputStream(); @@ -296,10 +333,10 @@ public class FileServiceImpl implements FileService { .stream(lowResInputStream, lowResInputStream.available(), -1) .contentType(image.getContentType()) .build()); - log.info("低分辨率版本已生成并上传: {}", lowResolutionObjectKey); } catch (Exception e) { log.error("生成低分辨率版本失败", e); + // 低分辨率生成失败不影响主流程 } } fileHashMapper.insertFileHash(new FileHash(imageInfo.getInstanceId(), hash)); 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 14569dd..8ca16cc 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 @@ -22,7 +22,14 @@