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 0d789b4..d9b6530 100644 --- a/src/main/java/com/bao/dating/config/WebConfig.java +++ b/src/main/java/com/bao/dating/config/WebConfig.java @@ -34,8 +34,8 @@ public class WebConfig implements WebMvcConfigurer { "/user/emailLogin", "/api/verification/send-email-code", "/ip/location", - "/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 799e9eb..8b5a374 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; /** @@ -83,4 +86,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 13b709a..d2bb7c8 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.UserInfoDTO; 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 4bed336..935f9b2 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 @@ -117,4 +118,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/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 6ecfe1b..d1bd5bc 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 媒体文件数组 @@ -344,4 +350,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 d41a2be..3e77e89 100644 --- a/src/main/resources/com/bao/dating/mapper/PostMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/PostMapper.xml @@ -135,5 +135,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 625a9fd..0f296e0 100644 --- a/src/main/resources/com/bao/dating/mapper/UserMapper.xml +++ b/src/main/resources/com/bao/dating/mapper/UserMapper.xml @@ -111,9 +111,9 @@ - +