|
|
@@ -0,0 +1,300 @@
|
|
|
+package org.springblade.business.utils;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.math.BigInteger;
|
|
|
+import java.net.HttpURLConnection;
|
|
|
+import java.net.URL;
|
|
|
+import java.nio.channels.Channels;
|
|
|
+import java.nio.channels.FileChannel;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.nio.file.Paths;
|
|
|
+import java.security.DigestInputStream;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 扩展的摘要算法工具类
|
|
|
+ * 新增对文件流、网络资源等的摘要计算支持
|
|
|
+ */
|
|
|
+public class DigestUtil {
|
|
|
+
|
|
|
+ private static final int BUFFER_SIZE = 8192;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算本地文件的MD5摘要
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @return 文件的MD5摘要
|
|
|
+ */
|
|
|
+ public static String md5OfFile(String filePath) throws IOException {
|
|
|
+ return digestOfFile(filePath, "MD5");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算本地文件的SHA-256摘要
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @return 文件的SHA-256摘要
|
|
|
+ */
|
|
|
+ public static String sha256OfFile(String filePath) throws IOException {
|
|
|
+ return digestOfFile(filePath, "SHA-256");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算本地文件的指定算法摘要
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @param algorithm 摘要算法
|
|
|
+ * @return 文件的摘要
|
|
|
+ */
|
|
|
+ public static String digestOfFile(String filePath, String algorithm) throws IOException {
|
|
|
+ Path path = Paths.get(filePath);
|
|
|
+ try (InputStream fis = Files.newInputStream(path)) {
|
|
|
+ return digestOfStream(fis, algorithm);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException("不支持的摘要算法: " + algorithm, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算MultipartFile的MD5摘要
|
|
|
+ * @param multipartFile MultipartFile对象
|
|
|
+ * @return 文件的MD5摘要
|
|
|
+ */
|
|
|
+ public static String md5OfMultipartFile(org.springframework.web.multipart.MultipartFile multipartFile) throws IOException {
|
|
|
+ return digestOfMultipartFile(multipartFile, "MD5");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算MultipartFile的SHA-256摘要
|
|
|
+ * @param multipartFile MultipartFile对象
|
|
|
+ * @return 文件的SHA-256摘要
|
|
|
+ */
|
|
|
+ public static String sha256OfMultipartFile(org.springframework.web.multipart.MultipartFile multipartFile) throws IOException {
|
|
|
+ return digestOfMultipartFile(multipartFile, "SHA-256");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算MultipartFile的指定算法摘要
|
|
|
+ * @param multipartFile MultipartFile对象
|
|
|
+ * @param algorithm 摘要算法
|
|
|
+ * @return 文件的摘要
|
|
|
+ */
|
|
|
+ public static String digestOfMultipartFile(org.springframework.web.multipart.MultipartFile multipartFile, String algorithm) throws IOException {
|
|
|
+ try (InputStream inputStream = multipartFile.getInputStream()) {
|
|
|
+ return digestOfStream(inputStream, algorithm);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException("不支持的摘要算法: " + algorithm, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算网络文件的MD5摘要
|
|
|
+ * @param fileUrl 网络文件URL
|
|
|
+ * @return 文件的MD5摘要
|
|
|
+ */
|
|
|
+ public static String md5OfUrl(String fileUrl) throws IOException {
|
|
|
+ return digestOfUrl(fileUrl, "MD5");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算网络文件的SHA-256摘要
|
|
|
+ * @param fileUrl 网络文件URL
|
|
|
+ * @return 文件的SHA-256摘要
|
|
|
+ */
|
|
|
+ public static String sha256OfUrl(String fileUrl) throws IOException {
|
|
|
+ return digestOfUrl(fileUrl, "SHA-256");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算网络文件的指定算法摘要
|
|
|
+ * @param fileUrl 网络文件URL
|
|
|
+ * @param algorithm 摘要算法
|
|
|
+ * @return 文件的摘要
|
|
|
+ */
|
|
|
+ public static String digestOfUrl(String fileUrl, String algorithm) throws IOException {
|
|
|
+ URL url = new URL(fileUrl);
|
|
|
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
|
+ connection.setRequestMethod("GET");
|
|
|
+ connection.connect();
|
|
|
+
|
|
|
+ try (InputStream inputStream = connection.getInputStream()) {
|
|
|
+ return digestOfStream(inputStream, algorithm);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException("不支持的摘要算法: " + algorithm, e);
|
|
|
+ } finally {
|
|
|
+ connection.disconnect();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算输入流的指定算法摘要
|
|
|
+ * @param inputStream 输入流
|
|
|
+ * @param algorithm 摘要算法
|
|
|
+ * @return 流的摘要
|
|
|
+ */
|
|
|
+ public static String digestOfStream(InputStream inputStream, String algorithm) throws IOException, NoSuchAlgorithmException {
|
|
|
+ MessageDigest md = MessageDigest.getInstance(algorithm);
|
|
|
+ try (DigestInputStream dis = new DigestInputStream(inputStream, md)) {
|
|
|
+ byte[] buffer = new byte[BUFFER_SIZE];
|
|
|
+ while (dis.read(buffer) != -1) {
|
|
|
+ // 读取数据的同时自动更新摘要
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return bytesToHex(md.digest());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算大文件的摘要(适用于内存受限情况)
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @param algorithm 摘要算法
|
|
|
+ * @return 文件的摘要
|
|
|
+ */
|
|
|
+ public static String digestOfLargeFile(String filePath, String algorithm) throws IOException {
|
|
|
+ Path path = Paths.get(filePath);
|
|
|
+ try (FileChannel channel = FileChannel.open(path);
|
|
|
+ InputStream inputStream = Channels.newInputStream(channel)) {
|
|
|
+ return digestOfStream(inputStream, algorithm);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException("不支持的摘要算法: " + algorithm, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * MD5摘要算法
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @return 32位MD5摘要
|
|
|
+ */
|
|
|
+ public static String md5(String input) {
|
|
|
+ return digest(input, "MD5");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SHA-1摘要算法
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @return SHA-1摘要
|
|
|
+ */
|
|
|
+ public static String sha1(String input) {
|
|
|
+ return digest(input, "SHA-1");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SHA-256摘要算法
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @return SHA-256摘要
|
|
|
+ */
|
|
|
+ public static String sha256(String input) {
|
|
|
+ return digest(input, "SHA-256");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SHA-512摘要算法
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @return SHA-512摘要
|
|
|
+ */
|
|
|
+ public static String sha512(String input) {
|
|
|
+ return digest(input, "SHA-512");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通用摘要算法
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @param algorithm 算法名称 MD5, SHA-1, SHA-256, SHA-512 ....
|
|
|
+ * @return 指定算法的摘要
|
|
|
+ */
|
|
|
+ private static String digest(String input, String algorithm) {
|
|
|
+ try {
|
|
|
+ MessageDigest md = MessageDigest.getInstance(algorithm);
|
|
|
+ byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
|
|
+ return bytesToHex(hashBytes);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException("不支持的摘要算法: " + algorithm, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通用摘要算法
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @param algorithm 算法名称
|
|
|
+ * @return 指定算法的摘要
|
|
|
+ */
|
|
|
+ public static String digest(byte[] input, String algorithm) {
|
|
|
+ try {
|
|
|
+ MessageDigest md = MessageDigest.getInstance(algorithm);
|
|
|
+ byte[] hashBytes = md.digest(input);
|
|
|
+ return bytesToHex(hashBytes);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException("不支持的摘要算法: " + algorithm, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 字节数组转十六进制字符串
|
|
|
+ * @param bytes 字节数组
|
|
|
+ * @return 十六进制字符串
|
|
|
+ */
|
|
|
+ public static String bytesToHex(byte[] bytes) {
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
+ for (byte b : bytes) {
|
|
|
+ result.append(String.format("%02x", b));
|
|
|
+ }
|
|
|
+ return result.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 十六进制字符串转字节数组
|
|
|
+ * @param hex 十六进制字符串
|
|
|
+ * @return 字节数组
|
|
|
+ */
|
|
|
+ public static byte[] hexToBytes(String hex) {
|
|
|
+ int len = hex.length();
|
|
|
+ byte[] data = new byte[len / 2];
|
|
|
+ for (int i = 0; i < len; i += 2) {
|
|
|
+ data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
|
|
|
+ + Character.digit(hex.charAt(i+1), 16));
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 字节数组转Base64字符串
|
|
|
+ * @param bytes 字节数组
|
|
|
+ * @return Base64编码字符串
|
|
|
+ */
|
|
|
+ public static String bytesToBase64(byte[] bytes) {
|
|
|
+ return java.util.Base64.getEncoder().encodeToString(bytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Base64字符串转字节数组
|
|
|
+ * @param base64 Base64编码字符串
|
|
|
+ * @return 字节数组
|
|
|
+ */
|
|
|
+ public static byte[] base64ToBytes(String base64) {
|
|
|
+ return java.util.Base64.getDecoder().decode(base64);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 字节数组转十进制字符串
|
|
|
+ * @param bytes 字节数组
|
|
|
+ * @return 十进制字符串表示
|
|
|
+ */
|
|
|
+ public static String bytesToDecimal(byte[] bytes) {
|
|
|
+ BigInteger bi = new BigInteger(1, bytes);
|
|
|
+ return bi.toString(10);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取指定长度的摘要(截取前length位)
|
|
|
+ * @param input 输入字符串
|
|
|
+ * @param algorithm 算法名称
|
|
|
+ * @param length 截取长度
|
|
|
+ * @return 截取后的摘要
|
|
|
+ */
|
|
|
+ public static String digestWithLength(String input, String algorithm, int length) {
|
|
|
+ String digest = digest(input, algorithm);
|
|
|
+ return digest.substring(0, Math.min(digest.length(), length));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|