Initial project
This commit is contained in:
1056
src/main/java/com/imyeyu/io/IO.java
Normal file
1056
src/main/java/com/imyeyu/io/IO.java
Normal file
File diff suppressed because it is too large
Load Diff
168
src/main/java/com/imyeyu/io/IOSize.java
Normal file
168
src/main/java/com/imyeyu/io/IOSize.java
Normal file
@ -0,0 +1,168 @@
|
||||
package com.imyeyu.io;
|
||||
|
||||
/**
|
||||
* 字节大小工具
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2023-06-01 16:41
|
||||
*/
|
||||
public class IOSize {
|
||||
|
||||
/**
|
||||
* 储存单位
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2022-04-08 14:37
|
||||
*/
|
||||
public enum Unit {
|
||||
|
||||
/** B */
|
||||
B,
|
||||
|
||||
/** KB */
|
||||
KB,
|
||||
|
||||
/** MB */
|
||||
MB,
|
||||
|
||||
/** GB */
|
||||
GB,
|
||||
|
||||
/** TB */
|
||||
TB,
|
||||
|
||||
/** PB */
|
||||
PB,
|
||||
|
||||
/** EB */
|
||||
EB;
|
||||
|
||||
/**
|
||||
* 转换指定单位字节量
|
||||
*
|
||||
* @param unit 单位
|
||||
* @param value 值
|
||||
* @return 该单位值字节量
|
||||
*/
|
||||
public static long value(Unit unit, double value) {
|
||||
Unit[] values = values();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i] == unit) {
|
||||
return (long) (value * Math.pow(1024, i));
|
||||
}
|
||||
}
|
||||
return (long) value;
|
||||
}
|
||||
}
|
||||
|
||||
/** 1 字节 */
|
||||
public static long BYTE = 1;
|
||||
|
||||
/** 1 KB */
|
||||
public static long KB = BYTE << 10;
|
||||
|
||||
/** 1 MB */
|
||||
public static long MB = KB << 10;
|
||||
|
||||
/** 1 GB */
|
||||
public static long GB = MB << 10;
|
||||
|
||||
/** 1 TB */
|
||||
public static long TB = GB << 10;
|
||||
|
||||
/** 1 PB */
|
||||
public static long PB = TB << 10;
|
||||
|
||||
/** 1 EB */
|
||||
public static long EB = PB << 10;
|
||||
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量,保留两位小数
|
||||
* <pre>
|
||||
* // 返回 100.01 KB
|
||||
* Tools.byteFormat(102411, 2);
|
||||
* </pre>
|
||||
*
|
||||
* @param size 字节大小
|
||||
* @return 格式化结果
|
||||
*/
|
||||
public static String format(double size) {
|
||||
return format(size, 2, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量
|
||||
* <pre>
|
||||
* // 返回 100.01 KB
|
||||
* Tools.byteFormat(102411, 2);
|
||||
* </pre>
|
||||
*
|
||||
* @param size 字节大小
|
||||
* @param stopUnit 最高等级,格式化到某单位后不再升级,最低 {@link Unit#B},最高 {@link Unit#EB}
|
||||
* @return 格式化结果
|
||||
*/
|
||||
public static String format(double size, Unit stopUnit) {
|
||||
return format(size, 2, stopUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量
|
||||
* <pre>
|
||||
* // 返回 100.01 KB
|
||||
* Tools.byteFormat(102411, 2);
|
||||
* </pre>
|
||||
*
|
||||
* @param size 字节大小
|
||||
* @param decimal 保留小数
|
||||
* @param stopUnit 最高等级,格式化到某单位后不再升级,最低 {@link Unit#B},最高 {@link Unit#EB}
|
||||
* @return 格式化结果
|
||||
*/
|
||||
public static String format(double size, int decimal, Unit stopUnit) {
|
||||
final Unit[] unit = Unit.values();
|
||||
if (0 < size) {
|
||||
for (int i = 0; i < unit.length; i++, size /= 1024d) {
|
||||
if (size <= 1000 || i == unit.length - 1 || unit[i] == stopUnit) {
|
||||
if (i == 0) {
|
||||
// 最小单位不需要小数
|
||||
return (int) size + " B";
|
||||
} else {
|
||||
String format = "%." + decimal + "f " + unit[i];
|
||||
return String.format(format, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "0 B";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量,不带单位
|
||||
* <pre>
|
||||
* // 返回 100.01
|
||||
* Tools.byteFormat(102411, 2);
|
||||
* </pre>
|
||||
*
|
||||
* @param size 字节大小
|
||||
* @param decimal 保留小数
|
||||
* @param stopUnit 最高等级,格式化到某单位后不再升级,最低 {@link Unit#B},最高 {@link Unit#TB}
|
||||
* @return 格式化结果(不带单位)
|
||||
*/
|
||||
public static String formatWithoutUnit(double size, int decimal, Unit stopUnit) {
|
||||
final Unit[] unit = Unit.values();
|
||||
if (0 < size) {
|
||||
for (int i = 0; i < unit.length; i++, size /= 1024d) {
|
||||
if (size <= 1000 || i == unit.length - 1 || unit[i] == stopUnit) {
|
||||
if (i == 0) {
|
||||
return String.valueOf((int) size);
|
||||
} else {
|
||||
String format = "%." + decimal + "f";
|
||||
return String.format(format, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
274
src/main/java/com/imyeyu/io/IOSpeedService.java
Normal file
274
src/main/java/com/imyeyu/io/IOSpeedService.java
Normal file
@ -0,0 +1,274 @@
|
||||
package com.imyeyu.io;
|
||||
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.CallbackArg;
|
||||
import com.imyeyu.utils.Calc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 字节速度计算,单例服务
|
||||
* <pre>
|
||||
* // 开始计算(默认周期 1 秒)
|
||||
* IOSpeedService.getInstance().start();
|
||||
*
|
||||
* // 监听全局字节量
|
||||
* IOSpeedService.getInstance().addBufferListener(d -> System.out.println(d));
|
||||
*
|
||||
* // 创建字节统计节点
|
||||
* IOSpeedService.item item = IOSpeedService.getInstance().createItem();
|
||||
*
|
||||
* // 监听节点字节量
|
||||
* item.addBufferListener(d -> System.out.println(d));
|
||||
*
|
||||
* // 推送字节量
|
||||
* item.push(1024);
|
||||
* </pre>
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-11-30 12:43
|
||||
*/
|
||||
public class IOSpeedService {
|
||||
|
||||
/** 单例对象 */
|
||||
private static IOSpeedService service;
|
||||
|
||||
/** 加盐,用于在数据对齐的 IO 流中看起来像非对齐传输。此操作会使计算数据变得非真实,仅作为 UI 显示时可使用 */
|
||||
private long salt;
|
||||
private Timer timer;
|
||||
private double totalBufferOld, totalBuffer;
|
||||
private boolean isRunning;
|
||||
|
||||
private final Map<String, Item> items;
|
||||
private final List<CallbackArg<Double>> bufferListeners;
|
||||
|
||||
private IOSpeedService() {
|
||||
items = new HashMap<>();
|
||||
bufferListeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** 启动服务,默认每秒计算一次 */
|
||||
public void start() {
|
||||
start(1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务
|
||||
*
|
||||
* @param interval 频率,单位:毫秒
|
||||
*/
|
||||
public void start(int interval) {
|
||||
if (timer != null) {
|
||||
shutdown();
|
||||
}
|
||||
timer = new Timer(true);
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// 单项速度
|
||||
Item value;
|
||||
for (Map.Entry<String, Item> item : items.entrySet()) {
|
||||
value = item.getValue();
|
||||
synchronized (value.bufferListeners) {
|
||||
long _salt = 0;
|
||||
if (value.buffer != value.bufferOld) {
|
||||
_salt = Calc.random(-salt, +salt);
|
||||
}
|
||||
for (int i = 0; i < value.bufferListeners.size(); i++) {
|
||||
value.bufferListeners.get(i).handler(value.buffer - value.bufferOld + _salt);
|
||||
}
|
||||
}
|
||||
value.bufferOld = value.buffer;
|
||||
}
|
||||
// 全局速度
|
||||
synchronized (bufferListeners) {
|
||||
long _salt = 0;
|
||||
if (totalBuffer != totalBufferOld) {
|
||||
_salt = Calc.random(-salt, +salt);
|
||||
}
|
||||
for (int i = 0; i < bufferListeners.size(); i++) {
|
||||
bufferListeners.get(i).handler(totalBuffer - totalBufferOld + _salt);
|
||||
}
|
||||
}
|
||||
totalBufferOld = totalBuffer;
|
||||
}
|
||||
}, 0, interval);
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建统计节点
|
||||
*
|
||||
* @return 统计节点
|
||||
*/
|
||||
public Item createItem() {
|
||||
return createItem(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建统计节点
|
||||
*
|
||||
* @param customID 自定义 ID
|
||||
* @return 统计节点
|
||||
*/
|
||||
public Item createItem(String customID) {
|
||||
Item item = new Item(customID);
|
||||
item.bufferListeners.add(d -> totalBuffer += d);
|
||||
items.put(item.id, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计节点
|
||||
*
|
||||
* @param id 节点 ID
|
||||
* @return 统计节点
|
||||
*/
|
||||
public Item item(String id) {
|
||||
return items.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取总缓冲量
|
||||
*
|
||||
* @return 缓冲数据量
|
||||
*/
|
||||
public double getTotalBuffer() {
|
||||
return totalBuffer;
|
||||
}
|
||||
|
||||
/** 重置计算 */
|
||||
public void reset() {
|
||||
totalBufferOld = totalBuffer = 0;
|
||||
}
|
||||
|
||||
/** 终止服务 */
|
||||
public void shutdown() {
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例对象
|
||||
*
|
||||
* @return 单例对象
|
||||
*/
|
||||
public static synchronized IOSpeedService getInstance() {
|
||||
if (service == null) {
|
||||
service = new IOSpeedService();
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否运行中
|
||||
*
|
||||
* @return true 为运行中
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
public long getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(long salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加全局字节量监听
|
||||
*
|
||||
* @param bufferListener 全局字节量监听
|
||||
*/
|
||||
public void addBufferListener(CallbackArg<Double> bufferListener) {
|
||||
synchronized (bufferListeners) {
|
||||
bufferListeners.add(bufferListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除全局字节量监听
|
||||
*
|
||||
* @param bufferListener 全局字节量监听
|
||||
*/
|
||||
public void removeBufferListener(CallbackArg<Double> bufferListener) {
|
||||
synchronized (bufferListeners) {
|
||||
bufferListeners.remove(bufferListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计节点,节点之间互相不受影响
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2023-05-09 10:34
|
||||
*/
|
||||
public static final class Item {
|
||||
|
||||
final List<CallbackArg<Double>> bufferListeners;
|
||||
|
||||
String id;
|
||||
double buffer;
|
||||
double bufferOld;
|
||||
|
||||
Item(String customID) {
|
||||
if (TimiJava.isEmpty(customID)) {
|
||||
id = UUID.randomUUID().toString();
|
||||
} else {
|
||||
id = customID;
|
||||
}
|
||||
buffer = 0;
|
||||
bufferListeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送已处理字节量
|
||||
*
|
||||
* @param buffer 字节量
|
||||
*/
|
||||
public void push(double buffer) {
|
||||
this.buffer += buffer;
|
||||
}
|
||||
|
||||
/** 重置计算 */
|
||||
public void reset() {
|
||||
bufferOld = buffer = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除字节量监听
|
||||
*
|
||||
* @param bufferListener 全局字节量监听
|
||||
*/
|
||||
public void addBufferListener(CallbackArg<Double> bufferListener) {
|
||||
synchronized (bufferListeners) {
|
||||
bufferListeners.add(bufferListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除字节量监听
|
||||
*
|
||||
* @param bufferListener 全局字节量监听
|
||||
*/
|
||||
public void removeBufferListener(CallbackArg<Double> bufferListener) {
|
||||
synchronized (bufferListeners) {
|
||||
bufferListeners.remove(bufferListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/main/java/com/imyeyu/io/JarReader.java
Normal file
87
src/main/java/com/imyeyu/io/JarReader.java
Normal file
@ -0,0 +1,87 @@
|
||||
package com.imyeyu.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* Jar 文件读取
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-12-01 17:39
|
||||
*/
|
||||
public class JarReader extends JarFile {
|
||||
|
||||
private final Map<String, InputStream> files;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*
|
||||
* @param file 文件
|
||||
* @throws IOException 读取异常
|
||||
*/
|
||||
public JarReader(File file) throws IOException {
|
||||
super(file);
|
||||
|
||||
files = new HashMap<>();
|
||||
|
||||
Enumeration<JarEntry> entries = entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (entry != null && !entry.isDirectory()) {
|
||||
files.put(entry.getName(), getInputStream(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 该文件是否存在
|
||||
*
|
||||
* @param path 路径
|
||||
* @return true 为存在
|
||||
*/
|
||||
public boolean has(String path) {
|
||||
return files.containsKey(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件输入流
|
||||
*
|
||||
* @param path 路径
|
||||
* @return 输入流
|
||||
* @throws FileNotFoundException 文件不存在
|
||||
*/
|
||||
public InputStream getInputStream(String path) throws FileNotFoundException {
|
||||
if (has(path)) {
|
||||
return files.get(path);
|
||||
} else {
|
||||
throw new FileNotFoundException("Not found file: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取某文件为字节数据
|
||||
*
|
||||
* @param path 路径
|
||||
* @return 字节数据
|
||||
* @throws FileNotFoundException 文件不存在
|
||||
*/
|
||||
public byte[] getBytes(String path) throws IOException {
|
||||
return IO.toBytes(getInputStream(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 jar 所有文件的数据流映射列表,Map<路径, 数据流>
|
||||
*
|
||||
* @return 数据流映射列表
|
||||
*/
|
||||
public Map<String, InputStream> getFiles() {
|
||||
return files;
|
||||
}
|
||||
}
|
||||
74
src/main/java/com/imyeyu/io/TreeFile.java
Normal file
74
src/main/java/com/imyeyu/io/TreeFile.java
Normal file
@ -0,0 +1,74 @@
|
||||
package com.imyeyu.io;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-06-30 16:26
|
||||
*/
|
||||
public class TreeFile extends File {
|
||||
|
||||
private List<TreeFile> children;
|
||||
|
||||
public TreeFile(String pathname) {
|
||||
super(pathname);
|
||||
|
||||
if (isDirectory()) {
|
||||
build();
|
||||
}
|
||||
}
|
||||
|
||||
private void build() {
|
||||
if (children != null) {
|
||||
return;
|
||||
}
|
||||
File[] files = listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
children = new ArrayList<>();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
TreeFile treeFile = new TreeFile(files[i].getAbsolutePath());
|
||||
children.add(treeFile);
|
||||
if (treeFile.isDirectory()) {
|
||||
treeFile.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void foreach(ForeachCallback callback) throws Exception {
|
||||
Stack<TreeFile> stack = new Stack<>();
|
||||
stack.push(this);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
TreeFile item = stack.pop();
|
||||
String relPath = item.getAbsolutePath().substring(Math.min(item.getAbsolutePath().length(), getAbsolutePath().length() + 1));
|
||||
callback.handler(relPath, item);
|
||||
if (TimiJava.isNotEmpty(item.children)) {
|
||||
for (int i = 0, l = item.children.size(); i < l; i++) {
|
||||
stack.push(item.children.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<TreeFile> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-12-09 23:00
|
||||
*/
|
||||
public interface ForeachCallback {
|
||||
|
||||
void handler(String relPath, File file) throws Exception;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user