springboot如何给上传的图片加水印,java工具类分享

我写了一个摄影网站,但是不太希望其他用户窃取别人的图片,需要业务中有一个加水印的功能

   /*** 图片文件上传*/@Autowiredprivate StringRedisTemplate redisTemplate;@PostMapping(value = "/imageUpload", name = "图片文件上传")public R imageUpload(MultipartFile file) {if (file.isEmpty()) {return R.error("上传异常");}try {// 计算上传图片的MD5值String md5 = calculateMD5(file);// 检查Redis中是否存在该MD5值Boolean exist = redisTemplate.opsForValue().setIfAbsent(md5, "1");if (exist != null && exist) {// 说明MD5值不存在,可以上传return R.ok(campusFileService.fileUploadImage(file));} else {return R.error("图片已存在,不能重复上传");}} catch (Exception e) {e.printStackTrace();return R.error("上传异常");}}private String calculateMD5(MultipartFile file) throws IOException {byte[] bytes = file.getBytes();return DigestUtil.md5Hex(bytes);}
  @Overridepublic CampusFileEntity fileUploadImage(MultipartFile file) {assertAllowed();if (file.getSize() > IMAGE_MAX_SIZE) {throw new FileSizeLimitExceededException(IMAGE_MAX_SIZE / 1024 / 1024);}try {String path = FileUploadUtils.upload(CAMPUS_FILE_PATH, file, MimeTypeUtils.IMAGE_EXTENSION);return saveDB(path);} catch (Exception e) {e.printStackTrace();throw new ServiceException(e.getMessage());}}
  /*** 文件上传** @param baseDir          相对应用的基目录* @param file             上传的文件* @param allowedExtension 上传文件类型* @return 返回上传成功的文件名* @throws FileSizeLimitExceededException       如果超出最大大小* @throws FileNameLengthLimitExceededException 文件名太长* @throws IOException                          比如读写文件出错时* @throws InvalidExtensionException            文件校验异常*/public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,InvalidExtensionException {int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);}assertAllowed(file, allowedExtension);String fileName = extractFilename(file);String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();file.transferTo(Paths.get(absPath));// 调用加水印方法ImgWatermarkUtil.imgWatermarkLocal(absPath, absPath,"plog");return getPathFileName(baseDir, fileName);}

加水印工具类

package com.oddfar.campus.framework.api.file;import cn.hutool.core.io.IoUtil;
import com.oddfar.campus.common.utils.DateUtils;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Random;
import java.util.stream.IntStream;/*** @program: 工具箱* @description 水印* @date 2023/6/6*/
public class ImgWatermarkUtil {public static void main(String[] args) throws Exception {//imgWatermarkLocal(""D:\\公司文件\\图片.png","D:\\公司文件\\图片1.png","这是水印");InputStream inputStream = new FileInputStream("D:\\公司文件\\图片1.png");InputStream inputStreamFile = imgWatermarkInt(inputStream, "yph", "这是水印BXCVVBADSSF","png");saveFile(inputStreamFile,"D:\\公司文件\\图片" + DateUtils.dateTimeNow() + ".png");}public static void testFile(InputStream inputStream) throws Exception {InputStream inputStreamFile = imgWatermarkInt(inputStream, "这是水印", "png");saveFile(inputStreamFile,"D:\\公司文件\\图片" + DateUtils.dateTimeNow() + ".png");}/*** 保存本地* @param inputStream 输入流* @param filePath 保存地址* @throws IOException*/public static void saveFile(InputStream inputStream, String filePath) throws IOException {try (OutputStream outputStream = new FileOutputStream(filePath)) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}}}/*** 给图片添加水印** @param inputFile     原文件路径+名称* @param outputFile    添加水印后输出文件保存的路径+名称* @param waterMarkName 添加水印的内容*/public static void imgWatermarkLocal(String inputFile, String outputFile, String waterMarkName) {try {BufferedImage img = ImageIO.read(new File(inputFile));int width = img.getWidth();int height = img.getHeight();int fontSize = (width + height) / 80;Graphics2D g = img.createGraphics();g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));g.rotate(0.2);// 间隔int split = fontSize * 2;// 文字占用的宽度int xWidth = getStrWidth(waterMarkName, fontSize);// x,y可以绘制的数量,多加一个补充空白int xCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;for (int j = 0; j < xCanNum; j++) {int x = xWidth * j + split * j;g.drawString(waterMarkName, x, y - (fontSize + split) * j);}}g.dispose();ImageIO.write(img, "png", new File(outputFile));} catch (IOException e) {throw new RuntimeException(e);}}/*** 图片加水印** @param inputStream   输入流* @param waterMarkName 添加水印的内容* @return OutputStream 输出流*/public static OutputStream imgWatermarkOut(InputStream inputStream, String waterMarkName) {try {BufferedImage img = ImageIO.read(inputStream);int width = img.getWidth();int height = img.getHeight();int fontSize = (width + height) / 80;Graphics2D g = img.createGraphics();g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));g.rotate(0.2);// 间隔int split = fontSize * 2;// 文字占用的宽度int xWidth = getStrWidth(waterMarkName, fontSize);// x,y可以绘制的数量,多加一个补充空白int xCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;for (int j = 0; j < xCanNum; j++) {int x = xWidth * j + split * j;g.drawString(waterMarkName, x, y - (fontSize + split) * j);}}g.dispose();ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ImageIO.write(img, "png", outputStream);return outputStream;} catch (IOException e) {throw new RuntimeException(e);}}/*** 图片加水印** @param inputStream   输入流* @param waterMarkName 添加水印的内容* @param fileSuffix    扩展名* @return InputStream 新的输入流*/public static InputStream imgWatermarkInt(InputStream inputStream, String waterMarkName, String fileSuffix) {try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {BufferedImage originalImage = ImageIO.read(inputStream);int width = originalImage.getWidth();int height = originalImage.getHeight();if (width <= 0 || height <= 0) {throw new IllegalArgumentException("Invalid image size.");}if (waterMarkName == null || waterMarkName.trim().isEmpty()) {throw new IllegalArgumentException("Watermark name cannot be empty.");}// 创建新的BufferedImage对象,并复制原始图像BufferedImage newImage = new BufferedImage(width, height, originalImage.getType());Graphics2D g = newImage.createGraphics();g.drawImage(originalImage, 0, 0, null);int fontSize = (width + height) / 80;g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));g.rotate(0.2);// 间隔int split = fontSize * 2;// 文字占用的宽度int xWidth = getStrWidth(waterMarkName, fontSize);// x,y可以绘制的数量,多加一个补充空白int xCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;for (int j = 0; j < xCanNum; j++) {int x = xWidth * j + split * j;g.drawString(waterMarkName, x, y - (fontSize + split) * j);}}g.dispose();// 将新的BufferedImage对象写入ByteArrayOutputStream中ImageIO.write(newImage, fileSuffix, outputStream);// 返回新的InputStream对象return new ByteArrayInputStream(outputStream.toByteArray());} catch (IOException e) {throw new RuntimeException(e);}}/*** 图片加水印** @param inputStream      输入流* @param waterMarkNameOne 添加水印的内容* @param waterMarkNameTwo 添加水印的内容* @param fileSuffix       扩展名* @return InputStream 输入流*/public static InputStream imgWatermarkInt(InputStream inputStream, String waterMarkNameOne, String waterMarkNameTwo, String fileSuffix) {try {BufferedImage img = ImageIO.read(inputStream);int width = img.getWidth();int height = img.getHeight();int fontSize = (width + height) / 80;// 间隔int split = fontSize * 2;// 文字占用的宽度int xWidth1 = getStrWidth(waterMarkNameOne, fontSize);int xWidth2 = getStrWidth(waterMarkNameTwo, fontSize);int xWidth = Math.max(xWidth1,xWidth2);// x,y可以绘制的数量,多加一个补充空白int xCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;String waterMarkName = "";for (int j = 0; j < xCanNum; j++) {Graphics2D g = img.createGraphics();g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));g.rotate(0.2);int x = xWidth * j + split * j;//随机水印if (j % 2 == 0) {waterMarkName = waterMarkNameOne;} else {waterMarkName = waterMarkNameTwo;}g.drawString(waterMarkName, x, y - (fontSize + split) * j);g.dispose();}}ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ImageIO.write(img, fileSuffix, outputStream);return new ByteArrayInputStream(outputStream.toByteArray());} catch (IOException e) {throw new RuntimeException(e);}}/*** 获取字符串占用的宽度* <br>** @param str      字符串* @param fontSize 文字大小* @return 字符串占用的宽度*/public static int getStrWidth(String str, int fontSize) {char[] chars = str.toCharArray();int fontSize2 = fontSize / 2;int width = 0;for (char c : chars) {int len = String.valueOf(c).getBytes().length;// 汉字为3,其余1// 可能还有一些特殊字符占用2等等,统统计为汉字if (len != 1) {width += fontSize;} else {width += fontSize2;}}return width == 0 ? 1 : width;}/*** 图片加水印** @param inputStream   输入流* @param waterMarkName 添加水印的内容* @param fileSuffix    扩展名* @return InputStream 新的输入流*/public static InputStream imgWatermarkInt2(InputStream inputStream, String waterMarkName, String fileSuffix) {try (InputStream in = inputStream; ByteArrayOutputStream out = new ByteArrayOutputStream()) {BufferedImage originalImage = ImageIO.read(in);int width = originalImage.getWidth();int height = originalImage.getHeight();if(width <= 0 || height <= 0){throw new IllegalArgumentException("Invalid image size.");}if(waterMarkName == null || waterMarkName.trim().isEmpty()){throw new IllegalArgumentException("Watermark name cannot be empty.");}// 创建新的BufferedImage对象,并复制原始图像BufferedImage newImage = new BufferedImage(width, height, originalImage.getType());Graphics2D g = newImage.createGraphics();g.drawImage(originalImage, 0, 0, null);int fontSize = (width + height) / 80;g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));g.rotate(0.2);// 间隔int split = fontSize * 2;// 文字占用的宽度int xWidth = getStrWidth(waterMarkName, fontSize);// x,y可以绘制的数量,多加一个补充空白int xCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;// 使用流式API进行操作IntStream.range(1, yCanNum + 1).forEach(i -> {int y = fontSize * i + split * i;IntStream.range(0, xCanNum).forEach(j -> {int x = xWidth * j + split * j;g.drawString(waterMarkName, x, y - (fontSize + split) * j);});});g.dispose();// 将新的BufferedImage对象写入ByteArrayOutputStream中ImageIO.write(newImage, fileSuffix, out);// 返回新的InputStream对象return new ByteArrayInputStream(out.toByteArray());} catch (IOException e) {throw new RuntimeException(e);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/793618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

离线 Linux 开发环境搭建

背景 无法连接外面的内网开发 通常需要打通如下&#xff1a; 虚拟机和网络模式VSCode 插件安装虚拟机软件安装 虚拟机和网络模式 虚拟机可以使用 Windows 自带的 Hyper-V 通常受限网络&#xff0c;网络模式更为复杂 Hyper-V 虚拟机有很多网络模式&#xff1a; 网络开机…

「33」如何让你的直播场景增加透视感?

「33」模糊滤镜增强背景画面透视感 在直播中,背景一直是作为一种陪衬而存在的,位于主场景的后面,其实,说得更直白一些,背景的存在就犹如“绿叶”,是为了衬托红花更加艳丽。所以…… 你通过画面背景的调整,可以从整体上对视频或图片的画面进行装饰,有助于增加画面的空间…

面试算法-127-优势洗牌

题目 给定两个长度相等的数组 nums1 和 nums2&#xff0c;nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。 返回 nums1 的任意排列&#xff0c;使其相对于 nums2 的优势最大化。 示例 1&#xff1a; 输入&#xff1a;nums1 [2,7,11,…

【C++】vector问题解决(非法的间接寻址,迭代器失效 , memcpy拷贝问题)

送给大家一句话&#xff1a; 世界在旋转&#xff0c;我们跌跌撞撞前进&#xff0c;这就够了 —— 阿贝尔 加缪 vector问题解决 1 前言2 迭代器区间拷贝3 迭代器失效问题4 memcpy拷贝问题 1 前言 我们之前实现了手搓vector&#xff0c;但是当时依然有些问题没有解决&#xff…

牛市股票还会亏钱?--外观模式

1.1 牛市股票还会亏钱&#xff1f; 炒股&#xff0c;碰到熊市&#xff0c;亏得一塌糊涂。 "我们公司的人现在都在炒股票&#xff0c;其实大部分人都不太懂&#xff0c;就是因为现在股市行情很火&#xff0c;于是都在跟风呢&#xff01;" 刚入市的人&#xff0c;什么都…

Day84:服务攻防-端口协议桌面应用QQWPS等RCEhydra口令猜解未授权检测

目录 端口协议-口令爆破&未授权 弱口令爆破 FTP&#xff1a;文件传输协议 RDP&#xff1a;Windows远程桌面协议 SSH&#xff1a;Linux安全外壳协议 未授权案例(rsync) 桌面应用-QQ&WPS&Clash QQ RCE 漏洞复现 WPS RCE 漏洞复现 Clas* RCE 漏洞复现 知识点…

【攻防世界】ics-05

php://filter 伪协议查看源码 preg_replace 函数漏洞 1.获取网页源代码。多点点界面&#xff0c;发现点云平台设备维护中心时&#xff0c;页面发生变化。 /?pageindex 输入什么显示什么&#xff0c;有回显。 用php://filter读取网页源代码 ?pagephp://filter/readconvert.…

设计模式 --5观察者模式

观察者模式 观察者模式的优缺点 优点 当一个对象改变的时候 需要同时改变其他对象的相关动作的时候 &#xff0c;而且它不知道有多少具体的对象需要改变 应该考虑使用观察者模式 。观察者模式的工作就是解除耦合 让耦合双方都依赖与抽象 而不是具体 是的各自改变都不会影响另…

【文献分享】ALKEMIE:加速材料发现和设计的智能计算平台

题目&#xff1a;ALKEMIE: An intelligent computational platform for accelerating materials discovery and design 链接&#xff1a;DOI: 10.1016/j.commatsci.2020.110064 ALKEMIE&#xff1a;加速材料发现和设计的智能计算平台 摘要 通过传统的试错方式开发具有目标特性…

ChatGPT 的核心 GPT 模型:探究其生成式预训练变换架构的革新与应用潜力

GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型是一种深度学习模型&#xff0c;由OpenAI于2018年首次提出&#xff0c;并在随后的几年中不断迭代发展&#xff0c;包括GPT-2、GPT-3以及最新的GPT-4。GPT模型在自然语言处理&#xff08;NLP&#xff09;领域…

领地选择

题源 二维前缀和 题目描述 作为在虚拟世界里统帅千军万马的领袖&#xff0c;小 Z 认为天时、地利、人和三者是缺一不可的&#xff0c;所以&#xff0c;谨慎地选择首都的位置对于小 Z 来说是非常重要的。 首都被认为是一个占地 CC 的正方形。小 Z 希望你寻找到一个合适的位置&am…

程序员如何赚美元的案例

偶然的契机让我走上了开发共享软件的道路。说起编写MP3 CD Maker&#xff0c;要追溯到1998年。当时国内掀起了VCD热潮&#xff0c;中关村很多家公司做起了VCD刻录软件的生意。 我也对这个市场很感兴趣&#xff0c;当时市面上刻录VCD的方法&#xff0c;是通过几种国外软件的组合…

ssm基于HTML5的出租车管理系统论文

摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管理就很关键。因此出租车信息的管…

Linux云计算之Linux基础3——Linux基本认识操作

1、终端 终端(terminal)&#xff1a;人和系统交互的必要设备&#xff0c;人机交互最后一个界面&#xff08;包含独立的输入输出设备&#xff09; 物理终端(console)&#xff1a;直接接入本机器的键盘设备和显示器虚拟终端(tty)&#xff1a;通过软件方式虚拟实现的终端。它可以…

C++相关概念和易错语法(4)(构造函数、析构函数)

一、构造函数 1.实现的功能&#xff1a;实例化对象的时候默认自动调用&#xff0c;相当于初始化。 条件&#xff1a;在书写时要满足构造函数的规范&#xff08;函数名 类名&#xff0c;不写返回值&#xff0c;也没有返回值&#xff09;&#xff0c;可以用inline来修饰。 2.自…

Spring中依赖注入的方法有几种,分别是什么?

依赖注入的目的&#xff1a; 都是为了减少对象之间的紧密耦合 1. 构造函数注入&#xff1a;通过在类的构造函数中接受依赖对象作为参数&#xff0c;Spring在创建对象时将依赖注入。 2. Setter方法注入&#xff1a;在类中提供setter方法&#xff0c;Spring通过调用这些setter方法…

Docker安装Memcached

要在Docker 中安装 Memcached&#xff0c;你可以使用官方提供的 Memcached 镜像。以下是安装的步骤&#xff1a; 首先确保已经安装了 Docker。如果还没有安装&#xff0c;请先安装 Docker。 使用以下命令从 Docker Hub 拉取 Memcached 镜像&#xff1a; docker pull memcach…

Qt模拟面试(超硬核)

1. 请简要介绍一下你的 Qt 开发经验。 建议&#xff1a;诚实地描述你的 Qt 经验&#xff0c;包括你使用过的 Qt 版本、开发过的项目类型、遇到的挑战以及如何解决它们。 假如你没有开发经验&#xff0c;可以提供一些关于 Qt 开发的一般信息和常见的经验分享。 Qt 是一个跨平…

4.6 offset指令,jmp short指令,far,dword ptr各种跳转指令

4.6 offset指令&#xff0c;jmp short指令&#xff0c;far&#xff0c;dword ptr各种跳转指令 可以修改IP&#xff0c;或同时修改CS和IP的指令统称为转移指令。概括的讲&#xff0c;转移指令就是可以控制CPU执行内存中某处代码的指令 1. 转移指令 1.1 8086CPU的转移行为有以…

按照指定的分隔符和次数从右侧开始分割字符串元素numpy.char.rsplit()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 按照指定的分隔符和次数 从右侧开始分割字符串元素 numpy.char.rsplit() [太阳]选择题 请问关于以下代码表述错误的选项是&#xff1f; import numpy as np a np.array([a b c, x,y,z, 1 2,…