我写了一个摄影网站,但是不太希望其他用户窃取别人的图片,需要业务中有一个加水印的功能
/*** 图片文件上传*/@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);}}}