245 lines
7.7 KiB
Java
245 lines
7.7 KiB
Java
package com.multictrl.common.utils;
|
||
|
||
import cn.hutool.core.collection.CollUtil;
|
||
import cn.hutool.core.util.StrUtil;
|
||
import cn.hutool.json.JSONArray;
|
||
import cn.hutool.json.JSONObject;
|
||
import cn.hutool.json.JSONUtil;
|
||
import com.drew.imaging.jpeg.JpegMetadataReader;
|
||
import com.drew.imaging.jpeg.JpegProcessingException;
|
||
import com.drew.imaging.mp4.Mp4MetadataReader;
|
||
import com.drew.metadata.Directory;
|
||
import com.drew.metadata.Metadata;
|
||
import com.drew.metadata.Tag;
|
||
import com.multictrl.common.exception.ExceptionUtils;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.apache.commons.io.FileUtils;
|
||
|
||
import java.io.File;
|
||
import java.io.IOException;
|
||
import java.math.BigDecimal;
|
||
import java.math.RoundingMode;
|
||
import java.security.MessageDigest;
|
||
import java.security.NoSuchAlgorithmException;
|
||
import java.text.ParseException;
|
||
import java.text.SimpleDateFormat;
|
||
import java.util.Date;
|
||
import java.util.List;
|
||
import java.util.Locale;
|
||
|
||
/**
|
||
* 通用工具
|
||
*
|
||
* @author Sdy
|
||
* @since 1.0.0 2026/4/24
|
||
*/
|
||
@Slf4j
|
||
public class Utils {
|
||
|
||
/**
|
||
* 线程休息n毫秒
|
||
*/
|
||
public static void sleep(long millis) {
|
||
try {
|
||
Thread.sleep(millis);
|
||
} catch (InterruptedException e) {
|
||
log.error("线程睡眠异常:{}", ExceptionUtils.getErrorStackTrace(e));
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 驼峰转下划线
|
||
*/
|
||
public static JSONObject beanToSnakeJson(Object obj) {
|
||
JSONObject camelJson = JSONUtil.parseObj(obj);
|
||
JSONObject snakeJson = new JSONObject();
|
||
for (String key : camelJson.keySet()) {
|
||
String snakeKey = StrUtil.toUnderlineCase(key); // 驼峰转下划线
|
||
Object value = camelJson.get(key);
|
||
// 递归处理嵌套 JSONObject 或 JSONArray
|
||
if (value instanceof JSONObject) {
|
||
value = beanToSnakeJson(value); // 递归转换
|
||
} else if (value instanceof JSONArray) {
|
||
JSONArray arr = new JSONArray();
|
||
for (Object item : (JSONArray) value) {
|
||
if (item instanceof JSONObject) {
|
||
arr.add(beanToSnakeJson(item));
|
||
} else {
|
||
arr.add(item);
|
||
}
|
||
}
|
||
value = arr;
|
||
}
|
||
snakeJson.set(snakeKey, value);
|
||
}
|
||
return snakeJson;
|
||
}
|
||
|
||
/**
|
||
* 获取最后两个字符
|
||
* "a/b/c/d" "c/d"
|
||
*/
|
||
public static String getLastTwoSegments(String str, String separator) {
|
||
if (StrUtil.isBlank(str)) return str;
|
||
// 按分隔符切割
|
||
List<String> parts = StrUtil.split(str, separator);
|
||
if (parts.size() <= 2) {
|
||
return str; // 不足两段则原样返回
|
||
} else {
|
||
// 取最后两段
|
||
List<String> lastTwo = parts.subList(parts.size() - 2, parts.size());
|
||
return CollUtil.join(lastTwo, String.valueOf(separator));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 文件转md5
|
||
*/
|
||
public static String md5File(String path) {
|
||
StringBuilder sb = new StringBuilder();
|
||
try {
|
||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||
md.update(FileUtils.readFileToByteArray(new File(path)));
|
||
byte[] b = md.digest();
|
||
int d;
|
||
for (byte value : b) {
|
||
d = value;
|
||
if (d < 0) {
|
||
d = value & 0xff;
|
||
// 与上一行效果等同
|
||
// i += 256;
|
||
}
|
||
if (d < 16) {
|
||
sb.append("0");
|
||
}
|
||
sb.append(Integer.toHexString(d));
|
||
}
|
||
} catch (NoSuchAlgorithmException | IOException e) {
|
||
log.error("md5 fail", e);
|
||
}
|
||
return sb.toString();
|
||
}
|
||
|
||
/**
|
||
* 获取文本的md5
|
||
*/
|
||
public static String md5Txt(String context) {
|
||
try {
|
||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||
byte[] buffer = context.getBytes();
|
||
md.update(buffer, 0, buffer.length);
|
||
byte[] digest = md.digest();
|
||
StringBuilder sb = new StringBuilder();
|
||
for (byte b : digest) {
|
||
sb.append(String.format("%02x", b));
|
||
}
|
||
return sb.toString();
|
||
} catch (NoSuchAlgorithmException exception) {
|
||
log.error("md5 fail", exception);
|
||
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 获取图片的Windows XP Keywords
|
||
*/
|
||
public static String getImageWindowsXpKeywords(String mediaPath) {
|
||
|
||
Metadata metadata;
|
||
try {
|
||
metadata = JpegMetadataReader.readMetadata(new File(mediaPath));
|
||
for (Directory exif : metadata.getDirectories()) {
|
||
for (Tag tag : exif.getTags()) {
|
||
if ("Windows XP Keywords".equals(tag.getTagName())) {
|
||
return tag.getDescription();
|
||
}
|
||
}
|
||
}
|
||
} catch (JpegProcessingException | IOException e) {
|
||
log.error("error", e);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
public static long getImageTime(String mediaPath) {
|
||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
|
||
Metadata metadata;
|
||
try {
|
||
metadata = JpegMetadataReader.readMetadata(new File(mediaPath));
|
||
|
||
for (Directory exif : metadata.getDirectories()) {
|
||
for (Tag tag : exif.getTags()) {
|
||
if ("Date/Time".equals(tag.getTagName())) {
|
||
return sdf.parse(tag.getDescription()).getTime();
|
||
}
|
||
}
|
||
}
|
||
} catch (JpegProcessingException | IOException e) {
|
||
log.error("error", e);
|
||
} catch (ParseException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
public static long getVideoTime(String videoPath) {
|
||
Metadata metadata;
|
||
try {
|
||
metadata = Mp4MetadataReader.readMetadata(new File(videoPath));
|
||
for (Directory exif : metadata.getDirectories()) {
|
||
for (Tag tag : exif.getTags()) {
|
||
if ("Creation Time".equals(tag.getTagName())) {
|
||
try {
|
||
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy",
|
||
Locale.US);
|
||
Date d = sdf.parse(tag.getDescription());
|
||
return d.getTime();
|
||
} catch (ParseException ignored) {
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (IOException e) {
|
||
log.error("error", e);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* 格式化double,保留指定位小数(四舍五入)
|
||
*
|
||
* @param value 原始数值
|
||
* @param scale 小数位数
|
||
* @return 格式化后的字符串
|
||
*/
|
||
private static String format(double value, int scale) {
|
||
if (Double.isNaN(value) || Double.isInfinite(value)) {
|
||
return "0";
|
||
}
|
||
BigDecimal bd = BigDecimal.valueOf(value);
|
||
bd = bd.setScale(scale, RoundingMode.HALF_UP);
|
||
return bd.stripTrailingZeros().toPlainString();
|
||
}
|
||
|
||
/**
|
||
* 字节数格式化为人性化字符串(自动选择B, KB, MB, GB, TB)
|
||
* 1024进制
|
||
*
|
||
* @param bytes 字节数
|
||
* @return 如 "1.45 GB"
|
||
*/
|
||
public static String formatBytes(long bytes) {
|
||
if (bytes < 0) {
|
||
return "0 B";
|
||
}
|
||
if (bytes < 1024) {
|
||
return bytes + " B";
|
||
}
|
||
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
||
char pre = "KMGTPE".charAt(exp - 1);
|
||
double result = bytes / Math.pow(1024, exp);
|
||
return format(result, 2) + " " + pre + "B";
|
||
}
|
||
}
|