前言
我想通过文件的md5生成关于这个md5的图像,类似于GitHub的随机像素头像,用处是让这个md5更加直观,也能用于生成各种用户头像,跟GitHub一样。
网上搜了一下,没有现成的方法,只能有一篇类似的文章可以借鉴一下,但是那篇是随机的字符串,而我的是文件,是固定的字符串,且不要改变列的数量,那我以此为基础,改一下就行了。
参考的内容:实现类似于Github的随机形状、随机颜色 像素风格头像_github像素头像_LLH_Durian的博客-CSDN博客
算法原理
由于md5是一个32位字符组成的字符串,那就可以再次上面大做文章了,我的计算方式为:
0~9位取平均值作为r(red),10~19位取平均值作为g(green),20~31位取平均值作为b(blue),那么头像的颜色就已经决定下来了。
接下来就是确定图像的像素数量,经过我的深思熟虑后,最终确定下来为8*16,即一共有8列像素,每列16行,由于头像是对称的,因此镜像一下,就是16*16的一张图片。
也就是如图所示的情况:
由于4个十六进制字符串正好是二进制的16位长度,正好可以铺满一列,我这边将1填充,0不填充,然后图片就能由此绘制出来了。
代码实现
代码有点长,不过很多都是注释,需要引入Hutool即可运行
package com.itdct.md5pic;import org.apache.commons.lang3.StringUtils;import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.Charset;import javax.imageio.ImageIO;import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.digest.MD5;/*** @author DCTANT* @version 1.0* @date 2023/4/28 15:55:23* @description 用于生成文件MD5图片的方法*/
public class GenerateMd5Pic {private MD5 md5 = new MD5();/*** 每个格子占据的像素大小*/private int blockSize = 250;/*** 内边距,默认为两倍的格子宽度*/private int padding = blockSize * 2;/*** 背景颜色,默认为白色*/private Color backgroundColor = new Color(255, 255, 255);/*** 输出路径*/private String outputPath;/*** 输入文件路径*/private String filePath;/*** 是否输出md5文件*/private boolean writeMd5File;/*** 通过32位长度的字符串生成图片** @param hexString*/public void string32Img(String hexString) {if (hexString.length() != 32) {throw new RuntimeException("输入字符串参数长度不是32");}// INFO: DCTANT: 2023/4/28 取RGB的总值String redTotal = hexString.substring(0, 10);String greenTotal = hexString.substring(10, 20);String blueTotal = hexString.substring(20, 32);// INFO: DCTANT: 2023/4/28 获取到平均后的rgb的值int r = getAverage256Value(redTotal);int g = getAverage256Value(greenTotal);int b = getAverage256Value(blueTotal);// INFO: DCTANT: 2023/4/28 定义每个格子的颜色Color blockColor = new Color(r, g, b);// INFO: DCTANT: 2023/4/28 计算图片的总像素宽度int picSize = 2 * padding + blockSize * 16;BufferedImage bufferedImage = new BufferedImage(picSize, picSize, BufferedImage.TYPE_INT_RGB);// INFO: DCTANT: 2023/4/28 获取图片画笔Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();// INFO: DCTANT: 2023/4/28 设置背景颜色graphics2D.setColor(backgroundColor);// INFO: DCTANT: 2023/4/28 画出整个背景graphics2D.fillRect(0, 0, picSize, picSize);boolean[][] blockArray = calculateBlockArray(hexString);graphics2D.setColor(blockColor);// INFO: DCTANT: 2023/4/28 绘制每个格子 for (int column = 0; column < blockArray.length; column++) {boolean[] rows = blockArray[column];for (int row = 0; row < rows.length; row++) {boolean isBlock = rows[row];if (!isBlock) {continue;}// INFO: DCTANT: 2023/4/28 数值为1的,画出方格int x = padding + column * blockSize;int y = padding + row * blockSize;graphics2D.fillRect(x, y, blockSize, blockSize);}}if (StringUtils.isBlank(outputPath)) {if (StringUtils.isBlank(filePath)) {outputPath = "./" + hexString + ".jpg";} else {outputPath = filePath + ".md5.jpg";}}try {File file = new File(outputPath);System.out.println("输出路径为:" + file.getAbsolutePath());ImageIO.write(bufferedImage, "JPG", new FileOutputStream(outputPath));//保存图片 JPEG表示保存格式} catch (IOException e) {e.printStackTrace();}}/*** 通过十六进制字符串,计算出16*16的像素数组** @param hexString* @return*/private boolean[][] calculateBlockArray(String hexString) {boolean[][] blockArray = new boolean[16][16];for (int column = 0; column < 8; column++) {// INFO: DCTANT: 2023/4/28 将32位的md5,每4位切一个下来String fourHexString = hexString.substring(column * 4, (column + 1) * 4);// INFO: DCTANT: 2023/4/28 转为十进制int decimal = HexUtil.hexToInt(fourHexString);// INFO: DCTANT: 2023/4/28 十进制转二进制StringBuilder binaryBuilder = new StringBuilder(Integer.toBinaryString(decimal));// INFO: DCTANT: 2023/4/28 补零if (binaryBuilder.length() < 16) {int addZeroCount = 16 - binaryBuilder.length();for (int i = 0; i < addZeroCount; i++) {binaryBuilder.insert(0, "0");}}// INFO: DCTANT: 2023/4/28 转为字符数组,用于判断是0还是1char[] chars = binaryBuilder.toString().toCharArray();for (int row = 0; row < chars.length; row++) {char theOneOrZero = chars[row];if (theOneOrZero == '1') {blockArray[column][row] = true;// INFO: DCTANT: 2023/4/28 对称点赋值blockArray[15 - column][row] = true;} else {blockArray[column][row] = false;// INFO: DCTANT: 2023/4/28 对称点赋值blockArray[15 - column][row] = false;}}}return blockArray;}/*** 通过文件生成其MD5图像** @param filePath*/public void fileImg(String filePath) {File file = new File(filePath);if (!file.exists()) {throw new RuntimeException("文件不存在");}String md5String = md5.digestHex(filePath);System.out.println("file md5 is " + md5String);if (writeMd5File) {FileUtil.writeString(md5String, filePath + ".md5", Charset.defaultCharset());}this.filePath = filePath;string32Img(md5String);}/*** 计算整个十六进制字符串的其中两位的平均值,并四舍五入** @param hex 该十六进制字符串每两位的平均值* @return*/public int getAverage256Value(String hex) {int loopCount = hex.length() / 2;if (hex.length() % 2 == 1) {throw new RuntimeException("hex长度必须为偶数");}double total = 0.0;for (int i = 0; i < loopCount; i++) {String twoHex = hex.substring(i * 2, (i + 1) * 2);int value = HexUtil.hexToInt(twoHex);total += value;}double value = total / loopCount;int result = new BigDecimal(value).setScale(0, RoundingMode.HALF_UP).intValue();return result;}public static void main(String[] args) {GenerateMd5Pic generateMd5Pic = new GenerateMd5Pic().setWriteMd5File(true);generateMd5Pic.fileImg("C:\\Tmp\test\\jenkins.war");}public String getFilePath() {return filePath;}public GenerateMd5Pic setFilePath(String filePath) {this.filePath = filePath;return this;}public int getBlockSize() {return blockSize;}public GenerateMd5Pic setBlockSize(int blockSize) {this.blockSize = blockSize;return this;}public int getPadding() {return padding;}public GenerateMd5Pic setPadding(int padding) {this.padding = padding;return this;}public String getOutputPath() {return outputPath;}public GenerateMd5Pic setOutputPath(String outputPath) {this.outputPath = outputPath;return this;}public GenerateMd5Pic setBackgroundColor(Color backgroundColor) {this.backgroundColor = backgroundColor;return this;}public boolean isWriteMd5File() {return writeMd5File;}public GenerateMd5Pic setWriteMd5File(boolean writeMd5File) {this.writeMd5File = writeMd5File;return this;}
}
实现结果
我就拿Jenkins的war包来举例吧,生成的效果如下:
如果你的Jenkins和我同一个版本的话,那么生成的图片应该是一模一样的,当然这个也可以用作头像