• 【原创】生成文件MD5图像,类似于GitHub的像素风格头像


    前言

    我想通过文件的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即可运行

    1. package com.itdct.md5pic;
    2. import org.apache.commons.lang3.StringUtils;
    3. import java.awt.Color;
    4. import java.awt.Graphics2D;
    5. import java.awt.image.BufferedImage;
    6. import java.io.File;
    7. import java.io.FileOutputStream;
    8. import java.io.IOException;
    9. import java.math.BigDecimal;
    10. import java.math.RoundingMode;
    11. import java.nio.charset.Charset;
    12. import javax.imageio.ImageIO;
    13. import cn.hutool.core.io.FileUtil;
    14. import cn.hutool.core.util.HexUtil;
    15. import cn.hutool.crypto.digest.MD5;
    16. /**
    17. * @author DCTANT
    18. * @version 1.0
    19. * @date 2023/4/28 15:55:23
    20. * @description 用于生成文件MD5图片的方法
    21. */
    22. public class GenerateMd5Pic {
    23. private MD5 md5 = new MD5();
    24. /**
    25. * 每个格子占据的像素大小
    26. */
    27. private int blockSize = 250;
    28. /**
    29. * 内边距,默认为两倍的格子宽度
    30. */
    31. private int padding = blockSize * 2;
    32. /**
    33. * 背景颜色,默认为白色
    34. */
    35. private Color backgroundColor = new Color(255, 255, 255);
    36. /**
    37. * 输出路径
    38. */
    39. private String outputPath;
    40. /**
    41. * 输入文件路径
    42. */
    43. private String filePath;
    44. /**
    45. * 是否输出md5文件
    46. */
    47. private boolean writeMd5File;
    48. /**
    49. * 通过32位长度的字符串生成图片
    50. *
    51. * @param hexString
    52. */
    53. public void string32Img(String hexString) {
    54. if (hexString.length() != 32) {
    55. throw new RuntimeException("输入字符串参数长度不是32");
    56. }
    57. // INFO: DCTANT: 2023/4/28 取RGB的总值
    58. String redTotal = hexString.substring(0, 10);
    59. String greenTotal = hexString.substring(10, 20);
    60. String blueTotal = hexString.substring(20, 32);
    61. // INFO: DCTANT: 2023/4/28 获取到平均后的rgb的值
    62. int r = getAverage256Value(redTotal);
    63. int g = getAverage256Value(greenTotal);
    64. int b = getAverage256Value(blueTotal);
    65. // INFO: DCTANT: 2023/4/28 定义每个格子的颜色
    66. Color blockColor = new Color(r, g, b);
    67. // INFO: DCTANT: 2023/4/28 计算图片的总像素宽度
    68. int picSize = 2 * padding + blockSize * 16;
    69. BufferedImage bufferedImage = new BufferedImage(picSize, picSize, BufferedImage.TYPE_INT_RGB);
    70. // INFO: DCTANT: 2023/4/28 获取图片画笔
    71. Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
    72. // INFO: DCTANT: 2023/4/28 设置背景颜色
    73. graphics2D.setColor(backgroundColor);
    74. // INFO: DCTANT: 2023/4/28 画出整个背景
    75. graphics2D.fillRect(0, 0, picSize, picSize);
    76. boolean[][] blockArray = calculateBlockArray(hexString);
    77. graphics2D.setColor(blockColor);
    78. // INFO: DCTANT: 2023/4/28 绘制每个格子
    79. for (int column = 0; column < blockArray.length; column++) {
    80. boolean[] rows = blockArray[column];
    81. for (int row = 0; row < rows.length; row++) {
    82. boolean isBlock = rows[row];
    83. if (!isBlock) {
    84. continue;
    85. }
    86. // INFO: DCTANT: 2023/4/28 数值为1的,画出方格
    87. int x = padding + column * blockSize;
    88. int y = padding + row * blockSize;
    89. graphics2D.fillRect(x, y, blockSize, blockSize);
    90. }
    91. }
    92. if (StringUtils.isBlank(outputPath)) {
    93. if (StringUtils.isBlank(filePath)) {
    94. outputPath = "./" + hexString + ".jpg";
    95. } else {
    96. outputPath = filePath + ".md5.jpg";
    97. }
    98. }
    99. try {
    100. File file = new File(outputPath);
    101. System.out.println("输出路径为:" + file.getAbsolutePath());
    102. ImageIO.write(bufferedImage, "JPG", new FileOutputStream(outputPath));//保存图片 JPEG表示保存格式
    103. } catch (IOException e) {
    104. e.printStackTrace();
    105. }
    106. }
    107. /**
    108. * 通过十六进制字符串,计算出16*16的像素数组
    109. *
    110. * @param hexString
    111. * @return
    112. */
    113. private boolean[][] calculateBlockArray(String hexString) {
    114. boolean[][] blockArray = new boolean[16][16];
    115. for (int column = 0; column < 8; column++) {
    116. // INFO: DCTANT: 2023/4/28 将32位的md5,每4位切一个下来
    117. String fourHexString = hexString.substring(column * 4, (column + 1) * 4);
    118. // INFO: DCTANT: 2023/4/28 转为十进制
    119. int decimal = HexUtil.hexToInt(fourHexString);
    120. // INFO: DCTANT: 2023/4/28 十进制转二进制
    121. StringBuilder binaryBuilder = new StringBuilder(Integer.toBinaryString(decimal));
    122. // INFO: DCTANT: 2023/4/28 补零
    123. if (binaryBuilder.length() < 16) {
    124. int addZeroCount = 16 - binaryBuilder.length();
    125. for (int i = 0; i < addZeroCount; i++) {
    126. binaryBuilder.insert(0, "0");
    127. }
    128. }
    129. // INFO: DCTANT: 2023/4/28 转为字符数组,用于判断是0还是1
    130. char[] chars = binaryBuilder.toString().toCharArray();
    131. for (int row = 0; row < chars.length; row++) {
    132. char theOneOrZero = chars[row];
    133. if (theOneOrZero == '1') {
    134. blockArray[column][row] = true;
    135. // INFO: DCTANT: 2023/4/28 对称点赋值
    136. blockArray[15 - column][row] = true;
    137. } else {
    138. blockArray[column][row] = false;
    139. // INFO: DCTANT: 2023/4/28 对称点赋值
    140. blockArray[15 - column][row] = false;
    141. }
    142. }
    143. }
    144. return blockArray;
    145. }
    146. /**
    147. * 通过文件生成其MD5图像
    148. *
    149. * @param filePath
    150. */
    151. public void fileImg(String filePath) {
    152. File file = new File(filePath);
    153. if (!file.exists()) {
    154. throw new RuntimeException("文件不存在");
    155. }
    156. String md5String = md5.digestHex(filePath);
    157. System.out.println("file md5 is " + md5String);
    158. if (writeMd5File) {
    159. FileUtil.writeString(md5String, filePath + ".md5", Charset.defaultCharset());
    160. }
    161. this.filePath = filePath;
    162. string32Img(md5String);
    163. }
    164. /**
    165. * 计算整个十六进制字符串的其中两位的平均值,并四舍五入
    166. *
    167. * @param hex 该十六进制字符串每两位的平均值
    168. * @return
    169. */
    170. public int getAverage256Value(String hex) {
    171. int loopCount = hex.length() / 2;
    172. if (hex.length() % 2 == 1) {
    173. throw new RuntimeException("hex长度必须为偶数");
    174. }
    175. double total = 0.0;
    176. for (int i = 0; i < loopCount; i++) {
    177. String twoHex = hex.substring(i * 2, (i + 1) * 2);
    178. int value = HexUtil.hexToInt(twoHex);
    179. total += value;
    180. }
    181. double value = total / loopCount;
    182. int result = new BigDecimal(value).setScale(0, RoundingMode.HALF_UP).intValue();
    183. return result;
    184. }
    185. public static void main(String[] args) {
    186. GenerateMd5Pic generateMd5Pic = new GenerateMd5Pic().setWriteMd5File(true);
    187. generateMd5Pic.fileImg("C:\\Tmp\test\\jenkins.war");
    188. }
    189. public String getFilePath() {
    190. return filePath;
    191. }
    192. public GenerateMd5Pic setFilePath(String filePath) {
    193. this.filePath = filePath;
    194. return this;
    195. }
    196. public int getBlockSize() {
    197. return blockSize;
    198. }
    199. public GenerateMd5Pic setBlockSize(int blockSize) {
    200. this.blockSize = blockSize;
    201. return this;
    202. }
    203. public int getPadding() {
    204. return padding;
    205. }
    206. public GenerateMd5Pic setPadding(int padding) {
    207. this.padding = padding;
    208. return this;
    209. }
    210. public String getOutputPath() {
    211. return outputPath;
    212. }
    213. public GenerateMd5Pic setOutputPath(String outputPath) {
    214. this.outputPath = outputPath;
    215. return this;
    216. }
    217. public GenerateMd5Pic setBackgroundColor(Color backgroundColor) {
    218. this.backgroundColor = backgroundColor;
    219. return this;
    220. }
    221. public boolean isWriteMd5File() {
    222. return writeMd5File;
    223. }
    224. public GenerateMd5Pic setWriteMd5File(boolean writeMd5File) {
    225. this.writeMd5File = writeMd5File;
    226. return this;
    227. }
    228. }

    实现结果

    我就拿Jenkins的war包来举例吧,生成的效果如下:

    如果你的Jenkins和我同一个版本的话,那么生成的图片应该是一模一样的,当然这个也可以用作头像

     

     

  • 相关阅读:
    c 几种父进程主动终止子进程的方法
    R语言—数据结构
    2333. 最小差值平方和-排序加二分查找,力扣c语言题解
    全局配置-window和tabBar
    SequoiaDB分布式数据库2022.10月刊
    AcWing第 68 场周赛题解
    _Linux 动态库
    智工教育:教编考试加分事项及报考条件
    LyScriptTools Control 调试类API手册
    利用IP地址信息提升网络安全
  • 原文地址:https://blog.csdn.net/DCTANT/article/details/130431798