Java實(shí)現(xiàn)根據(jù)圖片url下載圖片并動(dòng)態(tài)添加水印
前言
為了一些數(shù)據(jù)安全性,需要將圖片打上自定義的水印。水印需要滿足下列設(shè)置。
- 支持橫向或縱向。
- 支持單挑或多條水印。
- 支持自定義顏色配置。
- 支持水印字體大小配置與透明度配置。
相關(guān)依賴
僅需要引入下列依賴。
<!-- 用于處理URL請(qǐng)求獲取圖片 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.11.0</version>
</dependency>
完整代碼
package img;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;
import java.util.UUID;
/**
* 變量控制水印
*/
public class WaterMarkExample {
private static BufferedImage getBufferedImageByUrl(String imageUrl) throws IOException {
// 從URL獲取圖片
byte[] imageBytes = getImageBytesFromUrl(imageUrl);
BufferedImage image = ImageIO.read(new java.io.ByteArrayInputStream(imageBytes));
return image;
}
private static byte[] getImageBytesFromUrl(String imageUrl) throws IOException {
OkHttpClient client = UnsafeOkHttpClient.getUnsafeClient();
Request request = new Request.Builder()
.url(imageUrl)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().bytes();
}
}
private static byte[] convertImageToBytes(BufferedImage image) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "png", bos);
return bos.toByteArray();
}
/**
* 圖片追加水印
* @param image 原始圖片信息
* @param watermarkText 水印文字
* @param fontFilePath 字體文件
* @param isItalic 是否字體為斜體
* @param isSkew 水印是否傾斜
* @param isMultiLine 水印行數(shù)是否為多行
* @param waterMarkLineSize 水印行數(shù)
* @param waterMarkFontSize 水印字體大小
* @param alpha 透明度 [0,1]
* @param waterMarkColor 水印顏色 取值來(lái)源 java.awt.Color
* @return
*/
private static BufferedImage addWatermark(BufferedImage image, String watermarkText, String fontFilePath,
boolean isItalic, boolean isSkew, boolean isMultiLine,int waterMarkLineSize,float waterMarkFontSize,float alpha,Color waterMarkColor) {
BufferedImage watermarkedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = watermarkedImage.createGraphics();
g2d.drawImage(image, 0, 0, null);
try {
// 指定字體文件
Font customFont = Font.createFont(Font.TRUETYPE_FONT, new File(fontFilePath));
int fontStyle = isItalic? Font.ITALIC : Font.PLAIN;
Font derivedFont = customFont.deriveFont(fontStyle, waterMarkFontSize);
// 默認(rèn)演示使用,linux使用下面的代碼會(huì)加載失敗字體
//g2d.setFont(new Font("微軟雅黑", isItalic? Font.ITALIC : Font.PLAIN, 30));
g2d.setFont(derivedFont);
} catch (FontFormatException | IOException e) {
e.printStackTrace();
g2d.setFont(new Font("Arial", isItalic? Font.ITALIC : Font.PLAIN, 30));
}
// 水印的顏色
g2d.setColor(waterMarkColor);
AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(alphaComposite);
// 圖片的尺寸
int stringWidth = g2d.getFontMetrics().stringWidth(watermarkText);
int stringHeight = g2d.getFontMetrics().getHeight();
if (isMultiLine) {
// 多行
if (isSkew) {
// 旋轉(zhuǎn)角度
double rotationAngle = Math.toRadians(-45);
int xStep = stringWidth + 50;
int yStep = stringHeight + 50;
for (int y = 0; y < image.getHeight(); y += yStep) {
for (int x = 0; x < image.getWidth(); x += xStep) {
AffineTransform oldTransform = g2d.getTransform();
g2d.rotate(rotationAngle, x + stringWidth / 2, y + stringHeight / 2);
g2d.drawString(watermarkText, x, y);
g2d.setTransform(oldTransform);
}
}
} else {
waterMarkLineSize = Objects.isNull(waterMarkLineSize) ? 1:waterMarkLineSize;
int x = (image.getWidth() - stringWidth) / 2;
int y = (image.getHeight() - stringHeight) / 2;
for (int i = 0; i < waterMarkLineSize; i++) {
g2d.drawString(watermarkText, x, y + i * (stringHeight + 20));
}
}
} else {
// 單行
if (isSkew) {
double rotationAngle = Math.toRadians(-45);
int x = (image.getWidth() - stringWidth) / 2;
int y = (image.getHeight() - stringHeight) / 2;
AffineTransform oldTransform = g2d.getTransform();
g2d.rotate(rotationAngle, x + stringWidth / 2, y + stringHeight / 2);
g2d.drawString(watermarkText, x, y);
g2d.setTransform(oldTransform);
} else {
int x = (image.getWidth() - stringWidth) / 2;
int y = (image.getHeight() - stringHeight) / 2;
g2d.drawString(watermarkText, x, y);
}
}
g2d.dispose();
return watermarkedImage;
}
public static void saveImage(byte[] imageBytes, String filePath) throws IOException {
Path path = Path.of(filePath);
Files.write(path, imageBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}
public static void main(String[] args) throws IOException {
String imageUrl = "https://i-blog.csdnimg.cn/direct/d102cabed76c4cd9b74b9469f42a4353.png";
BufferedImage bufferedImageByUrl = getBufferedImageByUrl(imageUrl);
String fontFilePath = System.getProperty("user.dir")+"/fill2word"+"/font/simfang.ttf";
BufferedImage bufferedImage = addWatermark(bufferedImageByUrl,"香蕉集團(tuán)的香蕉不拿拿愛(ài)吃香蕉香蕉集團(tuán)的香蕉不拿拿愛(ài)吃香蕉香蕉集團(tuán)的香蕉不拿拿愛(ài)吃香蕉",fontFilePath,
false,false,true,3,50,0.3f,Color.blue);
byte[] watermarkedImageBytes = convertImageToBytes(bufferedImage);
String out_file_base_path = "doc/"+ UUID.randomUUID();
System.out.println(out_file_base_path);
saveImage(watermarkedImageBytes,out_file_base_path+".png");
}
}
一些補(bǔ)充
1、使用okhttp下載時(shí)提示ssl證書校驗(yàn)失敗
通常證書合法,直接使用下列代碼即可進(jìn)行http請(qǐng)求和下載。
OkHttpClient client = new OkHttpClient();
如果需要忽略ssl校驗(yàn),可以自定義OkHttpClient,如下所示:
import okhttp3.OkHttpClient;
import javax.net.ssl.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class UnsafeOkHttpClient {
public static OkHttpClient getUnsafeClient() {
try {
// 創(chuàng)建一個(gè)信任所有證書的 TrustManager
final X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
// 獲取 SSLContext 并初始化
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{trustManager}, new java.security.SecureRandom());
// 創(chuàng)建忽略證書的 SSLSocketFactory
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// 構(gòu)建 OkHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, trustManager);
builder.hostnameVerifier((hostname, session) -> true); // 忽略主機(jī)名驗(yàn)證
return builder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2、windows正常,但發(fā)布生產(chǎn)linux水印亂碼
一般出現(xiàn)此類問(wèn)題的根源在于linux環(huán)境不存在對(duì)應(yīng)的字體文件。
windows環(huán)境使用下列代碼進(jìn)行測(cè)試即可。
Graphics2D g2d = watermarkedImage.createGraphics();
g2d.drawImage(image, 0, 0, null);
// windows環(huán)境正常
g2d.setFont(new Font("微軟雅黑", isItalic? Font.ITALIC : Font.PLAIN, 30));
g2d.setFont(derivedFont);
但如果需要指定特定的字體文件,可以使用如下寫法。
Graphics2D g2d = watermarkedImage.createGraphics(); g2d.drawImage(image, 0, 0, null); // linux 環(huán)境指定特定的字體文件 Font customFont = Font.createFont(Font.TRUETYPE_FONT, new File(fontFilePath)); int fontStyle = isItalic? Font.ITALIC : Font.PLAIN; Font derivedFont = customFont.deriveFont(fontStyle, waterMarkFontSize); g2d.setFont(derivedFont);
字體文件可以從windows系統(tǒng)的C:\Windows\Fonts中進(jìn)行拷貝。
到此這篇關(guān)于Java實(shí)現(xiàn)根據(jù)圖片url下載圖片并動(dòng)態(tài)添加水印的文章就介紹到這了,更多相關(guān)Java根據(jù)圖片url下載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IntelliJ IDEA之配置JDK的4種方式(小結(jié))
這篇文章主要介紹了IntelliJ IDEA之配置JDK的4種方式(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
SpringBoot集成EasyExcel實(shí)現(xiàn)Excel導(dǎo)入的方法
這篇文章主要介紹了SpringBoot集成EasyExcel實(shí)現(xiàn)Excel導(dǎo)入的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
springmvc Rest風(fēng)格介紹及實(shí)現(xiàn)代碼示例
這篇文章主要介紹了springmvc Rest風(fēng)格介紹及實(shí)現(xiàn)代碼示例,rest風(fēng)格簡(jiǎn)潔,分享了HiddenHttpMethodFilter 的源碼,通過(guò)Spring4.0實(shí)現(xiàn)rest風(fēng)格源碼及簡(jiǎn)單錯(cuò)誤分析,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
SpringBoot實(shí)現(xiàn)啟動(dòng)類的存放位置
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)啟動(dòng)類的存放位置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Mybatis中如何設(shè)置sqlSession自動(dòng)提交
在MyBatis中,默認(rèn)情況下,獲取的SqlSession對(duì)象不會(huì)自動(dòng)提交事務(wù),這意味著在進(jìn)行更新、刪除或插入等操作后,需要顯式調(diào)用commit方法來(lái)提交事務(wù),但是,可以在獲取SqlSession時(shí)通過(guò)將openSession方法的參數(shù)設(shè)置為true2024-09-09
利用Java實(shí)現(xiàn)網(wǎng)站聚合工具
互聯(lián)網(wǎng)上有數(shù)以萬(wàn)億計(jì)的網(wǎng)站,每個(gè)網(wǎng)站大都具有一定的功能。搜索引擎雖然對(duì)互聯(lián)網(wǎng)上的部分網(wǎng)站建立了索引,但是其作為一個(gè)大而全的搜索系統(tǒng),無(wú)法很好的定位到一些特殊的需求。因此本文將介紹一個(gè)用java實(shí)現(xiàn)的網(wǎng)站數(shù)據(jù)聚合工具,需要的可以參考一下2022-01-01

