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