From 88abc41a9e809a0ae14467c542cb59f5a54863af Mon Sep 17 00:00:00 2001 From: yang Date: Wed, 7 Jan 2026 16:27:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=9B=BE=E7=89=87+=E6=B0=B4?= =?UTF-8?q?=E5=8D=B0(=E4=BD=9C=E8=80=85ID+=E4=BD=9C=E8=80=85=E5=90=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bao/dating/config/AliyunOSSConfig.java | 16 +++++ .../java/com/bao/dating/config/WebConfig.java | 3 +- .../bao/dating/controller/PostController.java | 19 ++++++ .../bao/dating/controller/UserController.java | 1 + .../com/bao/dating/mapper/PostMapper.java | 9 +++ .../java/com/bao/dating/pojo/entity/User.java | 4 +- .../com/bao/dating/service/PostService.java | 8 +++ .../dating/service/impl/PostServiceImpl.java | 63 ++++++++++++++++++ .../com/bao/dating/util/WatermarkUtil.java | 64 +++++++++++++++++++ .../com/bao/dating/mapper/PostMapper.xml | 6 ++ .../com/bao/dating/mapper/UserMapper.xml | 40 ++++++------ 11 files changed, 212 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/bao/dating/config/AliyunOSSConfig.java create mode 100644 src/main/java/com/bao/dating/util/WatermarkUtil.java diff --git a/src/main/java/com/bao/dating/config/AliyunOSSConfig.java b/src/main/java/com/bao/dating/config/AliyunOSSConfig.java new file mode 100644 index 0000000..ac0c56d --- /dev/null +++ b/src/main/java/com/bao/dating/config/AliyunOSSConfig.java @@ -0,0 +1,16 @@ +package com.bao.dating.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "aliyun.oss") +public class AliyunOSSConfig { + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + private String bucketName; + +} diff --git a/src/main/java/com/bao/dating/config/WebConfig.java b/src/main/java/com/bao/dating/config/WebConfig.java index dc377a7..ec89f7b 100644 --- a/src/main/java/com/bao/dating/config/WebConfig.java +++ b/src/main/java/com/bao/dating/config/WebConfig.java @@ -30,7 +30,8 @@ public class WebConfig implements WebMvcConfigurer { // 忽略的接口 .excludePathPatterns( "/user/login", - "/user/sendCode" + "/user/sendCode", + "/download/{postId}" ); } } diff --git a/src/main/java/com/bao/dating/controller/PostController.java b/src/main/java/com/bao/dating/controller/PostController.java index b4fb20b..9eab669 100644 --- a/src/main/java/com/bao/dating/controller/PostController.java +++ b/src/main/java/com/bao/dating/controller/PostController.java @@ -12,6 +12,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; import java.util.List; /** @@ -87,4 +90,20 @@ public class PostController { PostEditVO result = postService.updatePost(postId, postRequestDTO); return Result.success(ResultCode.SUCCESS, "动态更新成功", result); } + + @GetMapping("/download/{postId}") + public void downloadPostImage(@PathVariable Long postId, HttpServletResponse response) throws Exception { + try { + //Service 返回已经加好水印的图片 + BufferedImage image = postService.downloadWithWatermark(postId); + //设置响应头,触发浏览器下载 + response.setContentType("image/jpeg"); + response.setHeader("Content-Disposition", "attachment;filename=post_" + postId + ".jpg"); + //输出到浏览器 + ImageIO.write(image, "jpg", response.getOutputStream()); + response.getOutputStream().flush(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/controller/UserController.java b/src/main/java/com/bao/dating/controller/UserController.java index 7d65e65..fb3066d 100644 --- a/src/main/java/com/bao/dating/controller/UserController.java +++ b/src/main/java/com/bao/dating/controller/UserController.java @@ -6,6 +6,7 @@ import com.bao.dating.common.ResultCode; import com.bao.dating.context.UserContext; import com.bao.dating.pojo.dto.UserInfoUpdateDTO; import com.bao.dating.pojo.dto.UserLoginDTO; +import com.bao.dating.pojo.entity.User; import com.bao.dating.pojo.vo.UserInfoVO; import com.bao.dating.pojo.vo.UserLoginVO; import com.bao.dating.service.UserService; diff --git a/src/main/java/com/bao/dating/mapper/PostMapper.java b/src/main/java/com/bao/dating/mapper/PostMapper.java index 5a3ac72..b7461eb 100644 --- a/src/main/java/com/bao/dating/mapper/PostMapper.java +++ b/src/main/java/com/bao/dating/mapper/PostMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; +import java.util.Map; /** * 动态Mapper @@ -95,4 +96,12 @@ public interface PostMapper { * @return 影响行数 */ int decreaseFavoriteCount(Long postId); + + /** + * 根据动态id查询用户名和媒体信息 + * + * @param postId 动态id + * @return 用户名和媒体信息 + */ + Map getUsernameByUserId(Long postId); } diff --git a/src/main/java/com/bao/dating/pojo/entity/User.java b/src/main/java/com/bao/dating/pojo/entity/User.java index 482175a..9bf9641 100644 --- a/src/main/java/com/bao/dating/pojo/entity/User.java +++ b/src/main/java/com/bao/dating/pojo/entity/User.java @@ -44,7 +44,7 @@ public class User implements Serializable { private String userPhone; - private Double latitude; + private Double latitude; // 纬度 - private Double longitude; + private Double longitude; // 经度 } diff --git a/src/main/java/com/bao/dating/service/PostService.java b/src/main/java/com/bao/dating/service/PostService.java index b598daa..9f3071e 100644 --- a/src/main/java/com/bao/dating/service/PostService.java +++ b/src/main/java/com/bao/dating/service/PostService.java @@ -5,6 +5,7 @@ import com.bao.dating.pojo.entity.Post; import com.bao.dating.pojo.vo.PostEditVO; import org.springframework.web.multipart.MultipartFile; +import java.awt.image.BufferedImage; import java.util.List; /** @@ -55,4 +56,11 @@ public interface PostService { * @return 用户id */ Long selectUserIdByPostId(Long postId); + + /** + * 下载动态图片并添加水印 + * @param postId 动态ID + * @return 带水印的图片 + */ + BufferedImage downloadWithWatermark(Long postId) throws Exception; } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java b/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java index 2b47c9d..3ba9bb1 100644 --- a/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java +++ b/src/main/java/com/bao/dating/service/impl/PostServiceImpl.java @@ -10,11 +10,13 @@ import com.bao.dating.mapper.PostLikeMapper; import com.bao.dating.mapper.PostMapper; import com.bao.dating.pojo.dto.PostRequestDTO; import com.bao.dating.pojo.entity.Post; +import com.bao.dating.pojo.entity.User; import com.bao.dating.pojo.vo.PostEditVO; import com.bao.dating.service.PostService; import com.bao.dating.common.aliyun.AliOssUtil; import com.bao.dating.service.UserService; import com.bao.dating.util.FileUtil; +import com.bao.dating.util.WatermarkUtil; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -22,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; +import java.awt.image.BufferedImage; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; @@ -57,6 +60,9 @@ public class PostServiceImpl implements PostService { @Autowired private CommentsMapper commentsMapper; + @Autowired + private WatermarkUtil watermarkUtil; + /** * 上传媒体文件 * @param files 媒体文件数组 @@ -331,4 +337,61 @@ public class PostServiceImpl implements PostService { return postMapper.selectUserIdByPostId(postId); } + /** + * 下载动态图片并添加水印 + * + * @param postId 动态ID + * @return 带水印的图片 + * @throws Exception + */ + @Override + public BufferedImage downloadWithWatermark(Long postId) throws Exception { + // 一次性查出 动态图片 + 作者信息 + Map map = postMapper.getUsernameByUserId(postId); + + if (map == null || map.isEmpty()) { + Post post = postMapper.selectById(postId); + if (post == null) { + throw new RuntimeException("未找到指定postId的帖子: " + postId); + } + throw new RuntimeException("未找到与postId相关的用户和媒体信息: " + postId); + } + + String mediaUrl = (String) map.get("media_oss_keys"); + String username = (String) map.get("user_name"); + Object userIdObj = map.get("user_id"); + + if (mediaUrl == null || username == null || userIdObj == null) { + throw new RuntimeException("用户或媒体信息不完整: " + map); + } + + mediaUrl = mediaUrl.trim(); + if (mediaUrl.isEmpty()) { + throw new RuntimeException("媒体URL为空,postId: " + postId); + } + + Long userId = userIdObj instanceof Number + ? ((Number) userIdObj).longValue() + : Long.valueOf(userIdObj.toString()); + + // 解析 OSS ObjectKey(支持完整URL和直接存key两种) + String cleanUrl = mediaUrl.split("\\?")[0]; // 去掉 ? 后面的参数 + String objectKey; + + if (cleanUrl.startsWith("http")) { + // https://xxx.oss-cn-xxx.aliyuncs.com/post/xxx.jpg → post/xxx.jpg + objectKey = cleanUrl.substring(cleanUrl.indexOf(".com/") + 5); + } else { + objectKey = cleanUrl; + } + + if (objectKey.trim().isEmpty()) { + throw new RuntimeException("解析后的ObjectKey为空,url: " + mediaUrl); + } + + // 下载并动态加水印(只给下载的人看,OSS原图不改,数据库不动) + return watermarkUtil.downloadAndWatermark(objectKey, username, userId); + } + + } \ No newline at end of file diff --git a/src/main/java/com/bao/dating/util/WatermarkUtil.java b/src/main/java/com/bao/dating/util/WatermarkUtil.java new file mode 100644 index 0000000..23ddf07 --- /dev/null +++ b/src/main/java/com/bao/dating/util/WatermarkUtil.java @@ -0,0 +1,64 @@ + package com.bao.dating.util; + + import com.aliyun.oss.OSS; + import com.aliyun.oss.OSSClientBuilder; + import com.bao.dating.config.AliyunOSSConfig; + import com.bao.dating.mapper.PostMapper; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Component; + + import javax.imageio.ImageIO; + import java.awt.*; + import java.awt.image.BufferedImage; + import java.io.InputStream; + + @Component + public class WatermarkUtil { + + @Autowired + private AliyunOSSConfig aliyunOSSConfig; + + + public BufferedImage downloadAndWatermark(String objectKey, String username, Long userId) throws Exception { + OSS ossClient = new OSSClientBuilder().build( + aliyunOSSConfig.getEndpoint(), + aliyunOSSConfig.getAccessKeyId(), + aliyunOSSConfig.getAccessKeySecret() + ); + + InputStream inputStream = ossClient.getObject(aliyunOSSConfig.getBucketName(), objectKey).getObjectContent(); + BufferedImage image = ImageIO.read(inputStream); + Graphics2D g2d = image.createGraphics(); + + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + // 字体小一点 + Font font = new Font("微软雅黑", Font.BOLD, 24); + g2d.setFont(font); + + String text = "作者:" + username + " (ID:" + userId + ")"; + + FontMetrics fm = g2d.getFontMetrics(); + int textWidth = fm.stringWidth(text); + int textHeight = fm.getHeight(); + + // 右下角留边距 + int x = image.getWidth() - textWidth - 20; + int y = image.getHeight() - 20; + + // 黑色描边 + g2d.setColor(Color.BLACK); + g2d.drawString(text, x - 1, y - 1); + g2d.drawString(text, x + 1, y - 1); + g2d.drawString(text, x - 1, y + 1); + g2d.drawString(text, x + 1, y + 1); + + // 白色正文 + g2d.setColor(Color.WHITE); + g2d.drawString(text, x, y); + + g2d.dispose(); + ossClient.shutdown(); + return image; + } + } diff --git a/src/main/resources/com/bao/dating/mapper/PostMapper.xml b/src/main/resources/com/bao/dating/mapper/PostMapper.xml index d3c96bd..c594e5c 100644 --- a/src/main/resources/com/bao/dating/mapper/PostMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/PostMapper.xml @@ -122,5 +122,11 @@ + \ No newline at end of file diff --git a/src/main/resources/com/bao/dating/mapper/UserMapper.xml b/src/main/resources/com/bao/dating/mapper/UserMapper.xml index 8357c22..974d6c4 100644 --- a/src/main/resources/com/bao/dating/mapper/UserMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/UserMapper.xml @@ -5,22 +5,6 @@ - - - - - - - - - - - - - - - - SELECT @@ -60,7 +46,7 @@ updated_at, user_latitude, user_longitude - FROM dating.user WHERE user_id = #{userId} + FROM dating.user WHERE user_id = #{userId} @@ -96,8 +82,24 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file