java实现图片水印添加并自动上传七牛云

2024-06-18 13:28

本文主要是介绍java实现图片水印添加并自动上传七牛云,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

图片左下角水印添加

满足需求:可以对不同类型尺寸的照片、图片进行水印的添加,实现尺寸自适应添加水印。

水印效果

在这里插入图片描述

代码实现

Controller

package com.wlh.zetc.restore.controller;import cn.hutool.core.date.DateUtil;
import com.alibaba.nacos.client.utils.TenantUtil;
import com.wlh.zetc.admin.api.feign.RemoteTenantService;
import com.wlh.zetc.common.core.util.R;
import com.wlh.zetc.common.data.tenant.TenantBroker;
import com.wlh.zetc.common.data.tenant.TenantContextHolder;
import com.wlh.zetc.restore.service.QiniuService;
import com.wlh.zetc.restore.utils.ComplexWatermarkUtils;
import com.wlh.zetc.restore.utils.TextUtils;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;/*** 七牛云存储** @author wanghailin* @date 2024-03-12 10:19:56*/
@Controller
@RequestMapping("/qiniu" )
@Tag(description = "file" , name = "七牛云文件存储" )
@AllArgsConstructor
//@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class QiniuController
{private final QiniuService qiniuService;private final ComplexWatermarkUtils complexWatermarkUtils;/**** @param file* @param location* @param latitudeLongitude* @param remarks* @param weather* @param watermark 0:不添加水印 1:添加水印 2:添加水印但是不添加天气、位置、经纬度* @return* @throws IOException*/@PostMapping("/image/upload")@ResponseBodypublic R uploadImage(@RequestParam(value = "file", required = false) MultipartFile file,@RequestParam(value = "location", required = false) String location,@RequestParam(value = "latitudeLongitude", required = false) String latitudeLongitude,@RequestParam(value = "remarks", required = false) String remarks,@RequestParam(value = "weather", required = false) String weather,@RequestParam(value = "watermark", required = false,defaultValue = "1") Integer watermark) throws IOException {MultipartFile multipartFile = null;if (watermark != null && watermark.equals(0)){multipartFile = file;}else {multipartFile = complexWatermarkUtils.addComplexWatermark(file, DateUtil.now(), location, weather,latitudeLongitude,remarks,watermark);}System.out.println(multipartFile.getSize());if(!multipartFile.isEmpty()){InputStream in = multipartFile.getInputStream();String filename = multipartFile.getOriginalFilename();String project = "project"+ TenantContextHolder.getTenantId()+"/";try{String key = project+ TextUtils.generateFileName(filename);String imageUrl = qiniuService.uploadImage2qiniu(in,key);if(imageUrl!=null){HashMap<String, String> map = new HashMap<>();String privateDownloadUrl = qiniuService.getPrivateDownloadUrl(imageUrl);map.put("url",imageUrl);map.put("accessibleUrl",privateDownloadUrl);return R.ok(map).setMsg("上传成功");}}catch(IllegalArgumentException e){e.printStackTrace();}}return R.failed("上传失败");}@DeleteMapping("/image/delete")@ResponseBodypublic R deleteImage(@RequestParam("image")String imageUrl){boolean deleted = qiniuService.deleteImageFromQiniu(imageUrl);if(deleted){return R.ok(null,"删除成功");}return R.failed("删除失败");}@GetMapping("/image/download")@ResponseBodypublic R downloadImage(@RequestParam("image")String imageUrl){try {String privateDownloadUrl = qiniuService.getPrivateDownloadUrl(imageUrl);Map<String, String> map = new HashMap<>();map.put("url",privateDownloadUrl);return R.ok().setData(map);} catch (UnsupportedEncodingException e) {e.printStackTrace();}return R.ok();}
}

Utils

package com.wlh.zetc.restore.utils;import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.wlh.zetc.admin.api.entity.SysTenant;
import com.wlh.zetc.admin.api.feign.RemoteTenantService;
import com.wlh.zetc.admin.api.feign.RemoteUserService;
import com.wlh.zetc.common.core.util.R;
import com.wlh.zetc.common.data.tenant.TenantContextHolder;
import com.wlh.zetc.common.security.util.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;/*** 至农水印处理工具类** @author wanghailin* @date 2024-03-18 10:49:33*/
@Service
@AllArgsConstructor
public class ComplexWatermarkUtils {private final RemoteTenantService tenantService;//水印图片地址private static String watermarkImageURL = "https://qiniu.zjshjkj.com/logo/zhinong12.png";//水印添加public MultipartFile addComplexWatermark(MultipartFile file, String time, String location, String weather, String latitudeLongitude, String remarks,Integer watermark) {try {//MultipartFile multipartFile = correctImg(file);BufferedImage srcImg = null;srcImg = ImgUtil.read(file.getInputStream());Graphics2D g2d = srcImg.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);int watermarkAreaHeight = (int) (srcImg.getHeight() * 0.10);int watermarkAreaWide = (int) (srcImg.getWidth() * 0.01);BufferedImage watermarkImage = ImgUtil.read(getWatermarkImage(watermarkImageURL).getInputStream());int scaledWatermarkWidth = watermarkImage.getWidth() * watermarkAreaHeight / watermarkImage.getHeight();Image scale = ImgUtil.scale(watermarkImage, scaledWatermarkWidth, watermarkAreaHeight);int x = watermarkAreaWide;int y = watermarkAreaHeight * 7 - watermarkAreaHeight / 2;g2d.drawImage(scale, x, y, null);//获取当前项目名称R tenant = tenantService.getByTenantId(TenantContextHolder.getTenantId());SysTenant project = (SysTenant) tenant.getData();// 设置较大字体属性,仅用于 projectNameint projectNameFontSize = watermarkAreaHeight / 3;Font projectNameFont = new Font("宋体", Font.BOLD, projectNameFontSize);g2d.setFont(projectNameFont);g2d.setColor(Color.WHITE);// 将 projectName 分割为多行文本if(StringUtils.isNotEmpty(project.getName())){List<String> projectNameLines =splitTextToLines(project.getName(), 20);int projectNameY = y + watermarkAreaHeight +projectNameFontSize;for (String line : projectNameLines) {g2d.drawString(line, x, projectNameY);projectNameY += g2d.getFontMetrics().getHeight();}// 设置文本属性,根据水印区域大小调整字体大小int fontSize = watermarkAreaHeight / 5;Font font = new Font("宋体", Font.BOLD, fontSize);g2d.setFont(font);FontMetrics metrics = g2d.getFontMetrics(font);int textHeight = metrics.getHeight();// 绘制黄色竖杠g2d.setColor(Color.YELLOW);int barX = x;int barY = projectNameY - watermarkAreaHeight / 3; // 根据 projectName 的最终位置调整if(watermark.equals(2)){g2d.fillRect(barX, barY, textHeight /4, textHeight * 2 + 5 * 3);}else {g2d.fillRect(barX, barY, textHeight /4, textHeight * 5 + 5 * 3);}g2d.setColor(Color.WHITE);int textX = barX + watermarkAreaWide ;int textY = barY + textHeight;if(!StringUtils.isNotEmpty(remarks)){remarks = "";}if(watermark.equals(2)){String[] texts = new String[]{"时 间:"+time, "备 注:"+remarks};for (String text : texts) {g2d.drawString(text, textX, textY);textY += textHeight + 5;}}else {String[] texts = new String[]{"时 间:"+time, "地 点:"+location, "天 气:"+weather, "经纬度:"+latitudeLongitude, "备 注:"+remarks};for (String text : texts) {g2d.drawString(text, textX, textY);textY += textHeight + 5;}}}g2d.dispose();ByteArrayOutputStream baos = new ByteArrayOutputStream();try {ImageIO.write(srcImg, "png", baos);} catch (Exception e) {e.printStackTrace();return null;}MultipartFile resultFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), file.getContentType(), baos.toByteArray());IoUtil.close(baos);return resultFile;} catch (IOException e) {e.printStackTrace();return null;}}//文件下载public static MultipartFile getWatermarkImage(String fileUrl) {try {URL url = new URL(fileUrl);HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();httpURLConnection.setRequestMethod("GET");httpURLConnection.connect();String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);String contentType = httpURLConnection.getContentType();InputStream inputStream = httpURLConnection.getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while ((len = inputStream.read(buffer)) > -1) {baos.write(buffer, 0, len);}baos.flush();MultipartFile multipartFile = new MockMultipartFile(fileName, fileName, contentType, baos.toByteArray());inputStream.close();baos.close();return multipartFile;} catch (Exception e) {e.printStackTrace();return null;}}//图片矫正public static MultipartFile correctImg(MultipartFile srcFile) {File outFile = new File(srcFile.getName());try (FileOutputStream fos = new FileOutputStream(outFile)) {int angle = getAngle(srcFile);if (angle != 90 && angle != 270) {// todo 不需要旋转,直接返回return srcFile;} else {BufferedImage srcImg = ImageIO.read(srcFile.getInputStream());int imgWidth = srcImg.getHeight();int imgHeight = srcImg.getWidth();double centerWidth = ((double) imgWidth) / 2;double centerHeight = ((double) imgHeight) / 2;BufferedImage targetImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);Graphics2D g = targetImg.createGraphics();g.rotate(Math.toRadians(angle), centerWidth, centerHeight);g.drawImage(srcImg, (imgWidth - srcImg.getWidth()) / 2, (imgHeight - srcImg.getHeight()) / 2, null);g.rotate(Math.toRadians(-angle), centerWidth, centerHeight);g.dispose();// 输出图片ImageIO.write(targetImg, "jpg", fos);ByteArrayOutputStream baos = new ByteArrayOutputStream();try {ImgUtil.write(srcImg, srcFile.getContentType(), baos);} catch (Exception e) {e.printStackTrace();return null;}MultipartFile resultFile = new MockMultipartFile(srcFile.getName(), srcFile.getOriginalFilename(), srcFile.getContentType(), baos.toByteArray());IoUtil.close(baos);return resultFile;}} catch (Exception e) {e.printStackTrace();return null;}}//角度判断private static int getAngle(MultipartFile file) throws Exception {Metadata metadata = ImageMetadataReader.readMetadata(file.getInputStream());for (Directory directory : metadata.getDirectories()) {for (Tag tag : directory.getTags()) {if ("Orientation".equals(tag.getTagName())) {String orientation = tag.getDescription();if (orientation.contains("90")) {return 90;} else if (orientation.contains("180")) {return 180;} else if (orientation.contains("270")) {return 270;}}}}return 0;}// 辅助方法:将文本分割为多行private static List<String> splitTextToLines(String text, int maxCharsPerLine) {List<String> lines = new ArrayList<>();for (int start = 0; start < text.length(); start += maxCharsPerLine) {int end = Math.min(start + maxCharsPerLine, text.length());lines.add(text.substring(start, end));}return lines;}
}

七牛云service

package com.wlh.zetc.restore.service;import java.io.InputStream;
import java.io.UnsupportedEncodingException;/*** 七牛文件存储** @author wanghailin* @date 2024-03-12 10:21:35*/
public interface QiniuService {String uploadImage2qiniu(InputStream in, String key);boolean deleteImageFromQiniu(String key);String getPrivateDownloadUrl(String fileName) throws UnsupportedEncodingException;}

七牛云impl

package com.wlh.zetc.restore.service.impl;import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.*;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.wlh.zetc.restore.properties.QiniuProperties;
import com.wlh.zetc.restore.service.QiniuService;
import com.wlh.zetc.restore.utils.TextUtils;
import io.netty.channel.unix.Unix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;/*** 七牛文件存储** @author wanghailin* @date 2024-03-12 10:21:35*/
@Service
public class QiniuServiceImpl implements QiniuService
{private final String domain;private final String bucketName;private final String ak;private final String sk;// 七牛文件上传管理器private final Configuration cfg;private final Auth auth;@Autowiredpublic QiniuServiceImpl(QiniuProperties oss){this.ak = oss.getAccessKey();this.sk = oss.getSecretKey();this.domain = oss.getDomain(); // CDN域名this.bucketName = oss.getBucketName();// //构造一个带指定 Region 对象的配置类cfg = new Configuration(Zone.zone0());auth = Auth.create(ak,sk);}/*** 上传图片到七牛云* @return 图片url* */@Overridepublic String uploadImage2qiniu(InputStream in, String key){try {UploadManager uploadManager = new UploadManager(cfg);// 根据命名空间生成的上传tokenString upToken = auth.uploadToken(bucketName);Response response = uploadManager.put(in,key,upToken,null, null);//解析上传成功的结果DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);//System.out.println(putRet.key);//System.out.println(putRet.hash);//return String.format("http://%s/%s",this.domain,putRet.key);return putRet.key;} catch (QiniuException ex) {Response r = ex.response;System.err.println(r.toString());try {System.err.println(r.bodyString());} catch (QiniuException ex2) {//ignore}}return null;}/*** 删除图片* */@Overridepublic boolean deleteImageFromQiniu(String imageUrl){BucketManager bucketManager = new BucketManager(auth, cfg);try {String key= TextUtils.getKey(imageUrl);Response response = bucketManager.delete(bucketName,key);return response.isOK();} catch (QiniuException ex) {//如果遇到异常,说明删除失败System.err.println(ex.code());System.err.println(ex.response.toString());}return false;}/*** 获取文件下载路径** @param fileName* @return* @throws UnsupportedEncodingException*/public String getPrivateDownloadUrl(String fileName) throws UnsupportedEncodingException {//文件https访问配置DownloadUrl url = new DownloadUrl(domain, true, fileName);//DownloadUrl url = new DownloadUrl(domain, false, fileName);long expireInSeconds = 3600;//1小时,可以自定义链接过期时间long deadline = System.currentTimeMillis()/1000 + expireInSeconds;Auth auth = Auth.create(ak, sk);String urlString = null;try {urlString = url.buildURL(auth, deadline);} catch (QiniuException e) {throw new RuntimeException(e);}return urlString;}
}

七牛云配置类

package com.wlh.zetc.restore.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/*** 七牛文件存储** @author wanghailin* @date 2024-03-12 10:21:35*/
@Component
@ConfigurationProperties(prefix = "oss.qiniu")
@Data
public class QiniuProperties
{private String domain; // CDN域名private String accessKey; // ACCESS_KEYprivate String secretKey; // SECRET_KEYprivate String bucketName; // 空间名称
}

七牛云配置yml

oss:qiniu:domain: qiniu.znkj0215.com # 访问域名(正式访问域名地址) 暂未配置https
#    domain: qiniu.iswhl.com # 访问域名(测试访问域名地址) 已配置httpsaccessKey: APlM_0fW1A_PRS5bQ92rdGf9oSW-5q9mZK3Tv6yk # 公钥secretKey: Ri2eN9h4htBjZa8J8n_7QBfsAAvM_Arz5_CLqWth # 私钥bucketName: zhinonggengdi  #存储空间名称

这篇关于java实现图片水印添加并自动上传七牛云的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1072121

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传