generated from mingliqiye/lib-tem
feat(utils): 添加ByteBuffer变长数字编解码和防抖器功能
- 在ByteBufferUtils中添加getVarLong、getVarInt、getVarShort读取变长数字功能 - 在ByteBufferUtils中添加putVarLong、putVarInt、putVarShort写入变长数字功能 - 新增Debouncer防抖器类用于防止短时间内重复执行任务 - 更新build.gradle.kts中mybatis-plus-core依赖版本至3.5.15 - 优化FileUtils中文件操作方法,增加Path和File类型的扩展函数 - 添加字节转义字符常量定义和hex字符串转换工具方法
This commit is contained in:
parent
66ec71cbc7
commit
f2ee83ca64
@ -16,7 +16,7 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils
|
* ModuleName mingli-utils
|
||||||
* CurrentFile build.gradle.kts
|
* CurrentFile build.gradle.kts
|
||||||
* LastUpdate 2026-01-08 11:10:03
|
* LastUpdate 2026-01-14 13:01:44
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ dependencies {
|
|||||||
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58")
|
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58")
|
||||||
compileOnly("io.netty:netty-all:4.1.130.Final")
|
compileOnly("io.netty:netty-all:4.1.130.Final")
|
||||||
|
|
||||||
compileOnly("com.baomidou:mybatis-plus-core:3.0.1")
|
compileOnly("com.baomidou:mybatis-plus-core:3.5.15")
|
||||||
compileOnly("net.java.dev.jna:jna:5.17.0")
|
compileOnly("net.java.dev.jna:jna:5.17.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,13 +16,13 @@
|
|||||||
# ProjectName mingli-utils
|
# ProjectName mingli-utils
|
||||||
# ModuleName mingli-utils
|
# ModuleName mingli-utils
|
||||||
# CurrentFile gradle.properties
|
# CurrentFile gradle.properties
|
||||||
# LastUpdate 2026-01-10 08:55:10
|
# LastUpdate 2026-01-14 13:01:41
|
||||||
# UpdateUser MingLiPro
|
# UpdateUser MingLiPro
|
||||||
#
|
#
|
||||||
JDKVERSIONS=1.8
|
JDKVERSIONS=1.8
|
||||||
GROUPSID=com.mingliqiye.utils
|
GROUPSID=com.mingliqiye.utils
|
||||||
ARTIFACTID=mingli-utils
|
ARTIFACTID=mingli-utils
|
||||||
VERSIONS=4.3.3
|
VERSIONS=4.3.5
|
||||||
signing.keyId=B22AA93B
|
signing.keyId=B22AA93B
|
||||||
signing.password=
|
signing.password=
|
||||||
signing.secretKeyRingFile=secret.gpg
|
signing.secretKeyRingFile=secret.gpg
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 mingliqiye
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
* ProjectName mingli-utils
|
|
||||||
* ModuleName mingli-utils.main
|
|
||||||
* CurrentFile Main.kt
|
|
||||||
* LastUpdate 2026-01-06 14:04:14
|
|
||||||
* UpdateUser MingLiPro
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.mingliqiye.utils
|
|
||||||
|
|
||||||
import com.mingliqiye.utils.network.NetworkEndpoint
|
|
||||||
import com.mingliqiye.utils.uuid.UUID
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
val byteBuffer = ByteBuffer.allocate(320)
|
|
||||||
NetworkEndpoint
|
|
||||||
.of("0:65532")
|
|
||||||
.writeIpv4toByteBuffer(byteBuffer)
|
|
||||||
|
|
||||||
UUID.getMaxUUID().writeToByteBuffer(byteBuffer)
|
|
||||||
|
|
||||||
byteBuffer.flip()
|
|
||||||
|
|
||||||
println(NetworkEndpoint.ofIpv4(byteBuffer))
|
|
||||||
println(UUID.of(byteBuffer))
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -16,14 +16,16 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile ByteBufferUtils.kt
|
* CurrentFile ByteBufferUtils.kt
|
||||||
* LastUpdate 2026-01-07 10:01:45
|
* LastUpdate 2026-01-11 09:44:19
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:JvmName("ByteBufferUtil")
|
@file:JvmName("ByteBufferUtils")
|
||||||
|
|
||||||
package com.mingliqiye.utils.bytes
|
package com.mingliqiye.utils.bytes
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
@ -203,3 +205,93 @@ fun ByteBuffer.putBoolean(boolean: Boolean): ByteBuffer = this.put(if (boolean)
|
|||||||
fun ByteArray.toByteBuffer(): ByteBuffer {
|
fun ByteArray.toByteBuffer(): ByteBuffer {
|
||||||
return ByteBuffer.wrap(this)
|
return ByteBuffer.wrap(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从ByteBuffer中读取一个变长整数(VarNumber)。
|
||||||
|
*
|
||||||
|
* 变长整数使用可变长度编码方式,每个字节的最高位表示是否还有后续字节:
|
||||||
|
* - 如果最高位为1,则表示还有下一个字节;
|
||||||
|
* - 如果最高位为0,则表示当前字节是最后一个字节。
|
||||||
|
*
|
||||||
|
* @param size 最大允许读取的字节数,默认为8(即Long类型的最大长度)。
|
||||||
|
* @return 解码后的长整型数值。
|
||||||
|
* @throws IOException 当读取过程中发生IO异常时抛出。
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.getVarLong(size: Int = 8): Long {
|
||||||
|
var numRead = 0
|
||||||
|
var result: Long = 0
|
||||||
|
var read: Byte
|
||||||
|
do {
|
||||||
|
read = this.get()
|
||||||
|
// 将当前字节的有效7位数据左移相应位置后与结果进行按位或运算
|
||||||
|
result = result or ((read.toLong() and 127) shl (7 * numRead))
|
||||||
|
numRead++
|
||||||
|
if (numRead > size) {
|
||||||
|
throw IOException("VarNumber is too big")
|
||||||
|
}
|
||||||
|
} while ((read.toLong() and 128) != 0L)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从ByteBuffer中读取一个变长整数(VarNumber),返回Int类型。
|
||||||
|
*
|
||||||
|
* @return 解码后的整型数值。
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.getVarInt(): Int = this.getVarLong(4).toInt()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从ByteBuffer中读取一个变长整数(VarNumber),返回Short类型。
|
||||||
|
*
|
||||||
|
* @return 解码后的短整型数值。
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.getVarShort(): Short = this.getVarLong(2).toShort()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从输入流中读取一个变长长整数(VarLong),最大长度默认为8个字节。
|
||||||
|
*
|
||||||
|
* @return 解码后的长整型数值。
|
||||||
|
* @throws IOException 当读取过程中发生IO异常时抛出。
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun ByteBuffer.getVarLong(): Long = this.getVarLong(8)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将长整型数值编码为变长格式并写入ByteBuffer
|
||||||
|
*
|
||||||
|
* 变长整数使用可变长度编码方式,每个字节的最高位表示是否还有后续字节:
|
||||||
|
* - 如果数值还有更多字节,则最高位设为1;
|
||||||
|
* - 最后一个字节最高位设为0。
|
||||||
|
*
|
||||||
|
* @param value 要写入的长整型数值
|
||||||
|
* @return 当前ByteBuffer实例(支持链式调用)
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.putVarLong(value: Long): ByteBuffer {
|
||||||
|
var v = value
|
||||||
|
while (v >= 0x80) {
|
||||||
|
this.put((v and 0x7F or 0x80).toByte())
|
||||||
|
v = v shr 7
|
||||||
|
}
|
||||||
|
this.put(v.toByte())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将整型数值编码为变长格式并写入ByteBuffer
|
||||||
|
*
|
||||||
|
* @param value 要写入的整型数值
|
||||||
|
* @return 当前ByteBuffer实例(支持链式调用)
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.putVarInt(value: Int): ByteBuffer = this.putVarLong(value.toLong())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将短整型数值编码为变长格式并写入ByteBuffer
|
||||||
|
*
|
||||||
|
* @param value 要写入的短整型数值
|
||||||
|
* @return 当前ByteBuffer实例(支持链式调用)
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.putVarShort(value: Short): ByteBuffer = this.putVarLong(value.toLong())
|
||||||
|
|
||||||
|
|
||||||
|
fun ByteBuffer.writeStream(outputStream: OutputStream) = outputStream.write(this.toByteArray())
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2025 mingliqiye
|
* Copyright 2026 mingliqiye
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,28 +16,74 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile ByteUtils.kt
|
* CurrentFile ByteUtils.kt
|
||||||
* LastUpdate 2025-09-20 11:49:05
|
* LastUpdate 2026-01-14 13:01:44
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
@file:JvmName("ByteUtils")
|
@file:JvmName("ByteUtils")
|
||||||
|
|
||||||
package com.mingliqiye.utils.bytes
|
package com.mingliqiye.utils.bytes
|
||||||
|
|
||||||
|
import com.mingliqiye.utils.base.BASE16
|
||||||
import com.mingliqiye.utils.stream.SuperStream
|
import com.mingliqiye.utils.stream.SuperStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_ASC: Byte = 0x10
|
const val ESC_ASC: Byte = 0x10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_DESC: Byte = 0x1B
|
const val ESC_DESC: Byte = 0x1B
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_NONE: Byte = 0x00
|
const val ESC_NONE: Byte = 0x00
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_START: Byte = 0x01
|
const val ESC_START: Byte = 0x01
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_END: Byte = 0x02
|
const val ESC_END: Byte = 0x02
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_ESC: Byte = 0x03
|
const val ESC_ESC: Byte = 0x03
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_CONTROL: Byte = 0x04
|
const val ESC_CONTROL: Byte = 0x04
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_DATA: Byte = 0x05
|
const val ESC_DATA: Byte = 0x05
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符常量定义
|
||||||
|
* 定义了用于数据传输或协议通信中的特殊控制字节值
|
||||||
|
*/
|
||||||
const val ESC_RESERVED: Byte = 0x06
|
const val ESC_RESERVED: Byte = 0x06
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将字节数组转换为十六进制字符串列表
|
* 将字节数组转换为十六进制字符串列表
|
||||||
|
* @receiver 字节数组
|
||||||
* @return 包含每个字节对应十六进制字符串的列表
|
* @return 包含每个字节对应十六进制字符串的列表
|
||||||
*/
|
*/
|
||||||
fun ByteArray.getByteArrayString(): MutableList<String> {
|
fun ByteArray.getByteArrayString(): MutableList<String> {
|
||||||
@ -46,6 +92,12 @@ fun ByteArray.getByteArrayString(): MutableList<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将十六进制字符转换为对应的数值
|
||||||
|
* @receiver 十六进制字符
|
||||||
|
* @return 对应的数值(0-15)
|
||||||
|
* @throws NumberFormatException 当字符不是有效的十六进制字符时抛出
|
||||||
|
*/
|
||||||
fun Char.hexDigitToValue(): Int {
|
fun Char.hexDigitToValue(): Int {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
in '0'..'9' -> this - '0'
|
in '0'..'9' -> this - '0'
|
||||||
@ -55,6 +107,23 @@ fun Char.hexDigitToValue(): Int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hexStringToByteArray(string: String): ByteArray {
|
/**
|
||||||
|
* 将十六进制字符串转换为字节数组
|
||||||
|
*
|
||||||
|
* @param string 输入的十六进制字符串
|
||||||
|
* @return 转换后的字节数组
|
||||||
|
*/
|
||||||
|
fun hexStringToByteArray(string: String): ByteArray {
|
||||||
return string.hexToByteArray()
|
return string.hexToByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组转换为十六进制字符串表示。
|
||||||
|
*
|
||||||
|
* @param bytes 输入的字节数组
|
||||||
|
* @return 对应的十六进制字符串
|
||||||
|
*/
|
||||||
|
fun bytesToHex(bytes: ByteArray): String {
|
||||||
|
return BASE16.encode(bytes)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2025 mingliqiye
|
* Copyright 2026 mingliqiye
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,17 +15,17 @@
|
|||||||
*
|
*
|
||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile NumberUtils.kt
|
* CurrentFile StreamUtils.kt
|
||||||
* LastUpdate 2025-09-16 15:59:45
|
* LastUpdate 2026-01-11 09:41:14
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
|
@file:JvmName("StreamUtils")
|
||||||
|
|
||||||
@file:JvmName("NumberUtils")
|
package com.mingliqiye.utils.bytes
|
||||||
|
|
||||||
package com.mingliqiye.utils.number
|
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,25 +35,22 @@ import java.io.InputStream
|
|||||||
* - 如果最高位为1,则表示还有下一个字节;
|
* - 如果最高位为1,则表示还有下一个字节;
|
||||||
* - 如果最高位为0,则表示当前字节是最后一个字节。
|
* - 如果最高位为0,则表示当前字节是最后一个字节。
|
||||||
*
|
*
|
||||||
* @param input 输入流,用于读取数据。
|
|
||||||
* @param size 最大允许读取的字节数,默认为8(即Long类型的最大长度)。
|
* @param size 最大允许读取的字节数,默认为8(即Long类型的最大长度)。
|
||||||
* @return 解码后的长整型数值。
|
* @return 解码后的长整型数值。
|
||||||
* @throws IOException 当读取过程中发生IO异常或到达流末尾时抛出。
|
* @throws IOException 当读取过程中发生IO异常或到达流末尾时抛出。
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun readVarNumber(input: InputStream, size: Int = 10): Long {
|
fun InputStream.readVarNumber(size: Int = 8): Long {
|
||||||
var numRead = 0
|
var numRead = 0
|
||||||
var result: Long = 0
|
var result: Long = 0
|
||||||
var read: Byte
|
var read: Byte
|
||||||
do {
|
do {
|
||||||
read = input.read().let {
|
read = this.read().let {
|
||||||
if (it == -1) {
|
if (it == -1) {
|
||||||
throw IOException("Reached end of stream")
|
throw IOException("Reached end of stream")
|
||||||
}
|
}
|
||||||
it.toByte()
|
it.toByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将当前字节的有效7位数据左移相应位数,并与结果进行或运算
|
|
||||||
result = result or ((read.toLong() and 127) shl (7 * numRead))
|
result = result or ((read.toLong() and 127) shl (7 * numRead))
|
||||||
numRead++
|
numRead++
|
||||||
if (numRead > size) {
|
if (numRead > size) {
|
||||||
@ -66,35 +63,84 @@ fun readVarNumber(input: InputStream, size: Int = 10): Long {
|
|||||||
/**
|
/**
|
||||||
* 从输入流中读取一个变长整数(VarInt),最大长度限制为4个字节。
|
* 从输入流中读取一个变长整数(VarInt),最大长度限制为4个字节。
|
||||||
*
|
*
|
||||||
* @param input 输入流,用于读取数据。
|
|
||||||
* @return 解码后的整型数值。
|
* @return 解码后的整型数值。
|
||||||
* @throws IOException 当读取过程中发生IO异常时抛出。
|
* @throws IOException 当读取过程中发生IO异常时抛出。
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun readVarInt(input: InputStream): Int {
|
fun InputStream.readVarInt(): Int = this.readVarNumber(4).toInt()
|
||||||
return readVarNumber(input, size = 4).toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从输入流中读取一个变长短整数(VarShort),最大长度限制为2个字节。
|
* 从输入流中读取一个变长短整数(VarShort),最大长度限制为2个字节。
|
||||||
*
|
*
|
||||||
* @param input 输入流,用于读取数据。
|
|
||||||
* @return 解码后的短整型数值。
|
* @return 解码后的短整型数值。
|
||||||
* @throws IOException 当读取过程中发生IO异常时抛出。
|
* @throws IOException 当读取过程中发生IO异常时抛出。
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun readVarShort(input: InputStream): Short {
|
fun InputStream.readVarShort(): Short = this.readVarNumber(2).toShort()
|
||||||
return readVarNumber(input, size = 2).toShort()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从输入流中读取一个变长长整数(VarLong),最大长度默认为8个字节。
|
* 从输入流中读取一个变长长整数(VarLong),最大长度默认为8个字节。
|
||||||
*
|
*
|
||||||
* @param input 输入流,用于读取数据。
|
|
||||||
* @return 解码后的长整型数值。
|
* @return 解码后的长整型数值。
|
||||||
* @throws IOException 当读取过程中发生IO异常时抛出。
|
* @throws IOException 当读取过程中发生IO异常时抛出。
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun readVarLong(input: InputStream): Long {
|
fun InputStream.readVarLong(): Long = this.readVarNumber()
|
||||||
return readVarNumber(input)
|
|
||||||
|
/**
|
||||||
|
* 将长整型数值编码为变长格式并写入输出流
|
||||||
|
*
|
||||||
|
* 变长整数使用可变长度编码方式,每个字节的最高位表示是否还有后续字节:
|
||||||
|
* - 如果数值还有更多字节,则最高位设为1;
|
||||||
|
* - 最后一个字节最高位设为0。
|
||||||
|
*
|
||||||
|
* @param value 要写入的长整型数值
|
||||||
|
* @param size 最大允许写入的字节数,默认为8
|
||||||
|
* @throws IOException 当写入过程中发生IO异常时抛出
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun OutputStream.writeVarNumber(value: Long, size: Int = 8) {
|
||||||
|
var v = value
|
||||||
|
var numWritten = 0
|
||||||
|
|
||||||
|
while (v >= 0x80 && numWritten < size - 1) {
|
||||||
|
this.write((v and 0x7F or 0x80).toInt())
|
||||||
|
v = v ushr 7
|
||||||
|
numWritten++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (numWritten >= size) {
|
||||||
|
throw IOException("VarNumber is too big")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.write(v.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将整型数值编码为变长格式并写入输出流
|
||||||
|
*
|
||||||
|
* @param value 要写入的整型数值
|
||||||
|
* @throws IOException 当写入过程中发生IO异常时抛出
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun OutputStream.writeVarInt(value: Int): Unit = this.writeVarNumber(value.toLong(), 4)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将短整型数值编码为变长格式并写入输出流
|
||||||
|
*
|
||||||
|
* @param value 要写入的短整型数值
|
||||||
|
* @throws IOException 当写入过程中发生IO异常时抛出
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun OutputStream.writeVarShort(value: Short): Unit = this.writeVarNumber(value.toLong(), 2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将长整型数值编码为变长格式并写入输出流
|
||||||
|
*
|
||||||
|
* @param value 要写入的长整型数值
|
||||||
|
* @throws IOException 当写入过程中发生IO异常时抛出
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun OutputStream.writeVarLong(value: Long): Unit = this.writeVarNumber(value, 8)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2025 mingliqiye
|
* Copyright 2026 mingliqiye
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,7 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile FileUtils.kt
|
* CurrentFile FileUtils.kt
|
||||||
* LastUpdate 2025-09-15 09:12:47
|
* LastUpdate 2026-01-11 09:20:20
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
@file:JvmName("FileUtils")
|
@file:JvmName("FileUtils")
|
||||||
@ -24,195 +24,188 @@
|
|||||||
package com.mingliqiye.utils.file
|
package com.mingliqiye.utils.file
|
||||||
|
|
||||||
import com.mingliqiye.utils.path.OsPath
|
import com.mingliqiye.utils.path.OsPath
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.nio.file.StandardOpenOption
|
import java.nio.file.StandardOpenOption
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认字符集
|
* 默认字符集
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var DEFAULT_CHARSET: Charset = StandardCharsets.UTF_8
|
var DEFAULT_CHARSET: Charset = StandardCharsets.UTF_8
|
||||||
|
|
||||||
|
// 读取文件内容为字符串
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 读取文件内容为字符串
|
* 读取文件内容为字符串,使用默认字符集
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @return 文件内容的字符串表示
|
||||||
* @return 文件内容字符串
|
|
||||||
* @throws IOException 读取文件时发生错误
|
* @throws IOException 读取文件时发生错误
|
||||||
*/
|
*/
|
||||||
|
fun String.readFileToString(): String {
|
||||||
@Throws(IOException::class)
|
return this.readFileToString(DEFAULT_CHARSET)
|
||||||
fun readFileToString(filePath: String): String {
|
|
||||||
return readFileToString(filePath, DEFAULT_CHARSET)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 读取文件内容为字符串
|
* 读取文件内容为字符串
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param charset 字符编码格式
|
||||||
* @param charset 字符集
|
* @return 文件内容的字符串表示
|
||||||
* @return 文件内容字符串
|
|
||||||
* @throws IOException 读取文件时发生错误
|
* @throws IOException 读取文件时发生错误
|
||||||
*/
|
*/
|
||||||
|
fun String.readFileToString(charset: Charset): String {
|
||||||
@Throws(IOException::class)
|
val path = OsPath.of(this)
|
||||||
fun readFileToString(filePath: String, charset: Charset): String {
|
|
||||||
val path = OsPath.of(filePath)
|
|
||||||
val bytes = Files.readAllBytes(path)
|
val bytes = Files.readAllBytes(path)
|
||||||
return String(bytes, charset)
|
return String(bytes, charset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将字符串写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字符串写入文件
|
* 将字符串写入文件,使用默认字符集
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param content 要写入的字符串内容
|
||||||
* @param content 要写入的内容
|
|
||||||
* @throws IOException 写入文件时发生错误
|
* @throws IOException 写入文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.writeStringToFile(content: String) {
|
||||||
fun writeStringToFile(filePath: String, content: String) {
|
this.writeStringToFile(content, DEFAULT_CHARSET)
|
||||||
writeStringToFile(filePath, content, DEFAULT_CHARSET)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字符串写入文件
|
* 将字符串写入文件
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param content 要写入的字符串内容
|
||||||
* @param content 要写入的内容
|
* @param charset 字符编码格式
|
||||||
* @param charset 字符集
|
|
||||||
* @throws IOException 写入文件时发生错误
|
* @throws IOException 写入文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.writeStringToFile(content: String, charset: Charset) {
|
||||||
fun writeStringToFile(filePath: String, content: String, charset: Charset) {
|
val path = Paths.get(this)
|
||||||
val path = Paths.get(filePath)
|
|
||||||
path.parent?.let { Files.createDirectories(it) }
|
path.parent?.let { Files.createDirectories(it) }
|
||||||
Files.write(path, content.toByteArray(charset))
|
Files.write(path, content.toByteArray(charset))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字符串列表(按行分割)
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 读取文件内容为字符串列表(按行分割)
|
* 读取文件内容为字符串列表(按行分割),使用默认字符集
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @return 按行分割的字符串列表
|
||||||
* @return 文件内容按行分割的字符串列表
|
|
||||||
* @throws IOException 读取文件时发生错误
|
* @throws IOException 读取文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.readLines(): List<String> {
|
||||||
fun readLines(filePath: String): List<String> {
|
return this.readLines(DEFAULT_CHARSET)
|
||||||
return readLines(filePath, DEFAULT_CHARSET)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 读取文件内容为字符串列表(按行分割)
|
* 读取文件内容为字符串列表(按行分割)
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param charset 字符编码格式
|
||||||
* @param charset 字符集
|
* @return 按行分割的字符串列表
|
||||||
* @return 文件内容按行分割的字符串列表
|
|
||||||
* @throws IOException 读取文件时发生错误
|
* @throws IOException 读取文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.readLines(charset: Charset): List<String> {
|
||||||
fun readLines(filePath: String, charset: Charset): List<String> {
|
val path = Paths.get(this)
|
||||||
val path = Paths.get(filePath)
|
|
||||||
return Files.readAllLines(path, charset)
|
return Files.readAllLines(path, charset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将字符串列表写入文件(每行一个元素)
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字符串列表写入文件(每行一个元素)
|
* 将字符串列表写入文件(每行一个元素),使用默认字符集
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param lines 要写入的字符串列表
|
||||||
* @param lines 要写入的行内容列表
|
|
||||||
* @throws IOException 写入文件时发生错误
|
* @throws IOException 写入文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.writeLines(lines: List<String>) {
|
||||||
fun writeLines(filePath: String, lines: List<String>) {
|
this.writeLines(lines, DEFAULT_CHARSET)
|
||||||
writeLines(filePath, lines, DEFAULT_CHARSET)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字符串列表写入文件(每行一个元素)
|
* 将字符串列表写入文件(每行一个元素)
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param lines 要写入的字符串列表
|
||||||
* @param lines 要写入的行内容列表
|
* @param charset 字符编码格式
|
||||||
* @param charset 字符集
|
|
||||||
* @throws IOException 写入文件时发生错误
|
* @throws IOException 写入文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.writeLines(lines: List<String>, charset: Charset) {
|
||||||
fun writeLines(filePath: String, lines: List<String>, charset: Charset) {
|
val path = Paths.get(this)
|
||||||
val path = Paths.get(filePath)
|
|
||||||
Files.createDirectories(path.parent)
|
Files.createDirectories(path.parent)
|
||||||
Files.write(path, lines, charset)
|
Files.write(path, lines, charset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 复制文件
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 复制文件
|
* 复制文件到目标路径
|
||||||
*
|
*
|
||||||
* @param sourcePath 源文件路径
|
|
||||||
* @param targetPath 目标文件路径
|
* @param targetPath 目标文件路径
|
||||||
* @throws IOException 复制文件时发生错误
|
* @throws IOException 复制文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.copyFile(targetPath: String) {
|
||||||
fun copyFile(sourcePath: String, targetPath: String) {
|
val source = Paths.get(this)
|
||||||
val source = Paths.get(sourcePath)
|
|
||||||
val target = Paths.get(targetPath)
|
val target = Paths.get(targetPath)
|
||||||
Files.createDirectories(target.parent)
|
Files.createDirectories(target.parent)
|
||||||
Files.copy(source, target)
|
Files.copy(source, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
/**
|
/**
|
||||||
* 删除文件
|
* 删除文件
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @return 删除操作是否成功
|
||||||
* @return 如果文件删除成功返回true,否则返回false
|
|
||||||
*/
|
*/
|
||||||
fun deleteFile(filePath: String): Boolean {
|
fun String.deleteFile(): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val path = Paths.get(filePath)
|
val path = Paths.get(this)
|
||||||
Files.deleteIfExists(path)
|
Files.deleteIfExists(path)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
/**
|
/**
|
||||||
* 检查文件是否存在
|
* 检查文件是否存在
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @return 文件是否存在
|
||||||
* @return 如果文件存在返回true,否则返回false
|
|
||||||
*/
|
*/
|
||||||
fun exists(filePath: String): Boolean {
|
fun String.exists(): Boolean {
|
||||||
val path = Paths.get(filePath)
|
val path = Paths.get(this)
|
||||||
return Files.exists(path)
|
return Files.exists(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取文件大小
|
||||||
/**
|
/**
|
||||||
* 获取文件大小
|
* 获取文件大小
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @return 文件大小(字节),如果获取失败则返回-1
|
||||||
* @return 文件大小(字节),如果文件不存在返回-1
|
|
||||||
*/
|
*/
|
||||||
|
fun String.getFileSize(): Long {
|
||||||
fun getFileSize(filePath: String): Long {
|
|
||||||
return try {
|
return try {
|
||||||
val path = Paths.get(filePath)
|
val path = Paths.get(this)
|
||||||
Files.size(path)
|
Files.size(path)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建目录
|
||||||
/**
|
/**
|
||||||
* 创建目录
|
* 创建目录
|
||||||
*
|
*
|
||||||
* @param dirPath 目录路径
|
* @return 创建操作是否成功
|
||||||
* @return 如果目录创建成功返回true,否则返回false
|
|
||||||
*/
|
*/
|
||||||
|
fun String.createDirectory(): Boolean {
|
||||||
fun createDirectory(dirPath: String): Boolean {
|
|
||||||
return try {
|
return try {
|
||||||
val path = Paths.get(dirPath)
|
val path = Paths.get(this)
|
||||||
Files.createDirectories(path)
|
Files.createDirectories(path)
|
||||||
true
|
true
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
@ -220,97 +213,61 @@ fun createDirectory(dirPath: String): Boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 读取文件内容为字节数组
|
||||||
* 获取文件扩展名
|
@Throws(IOException::class)
|
||||||
*
|
|
||||||
* @param fileName 文件名
|
|
||||||
* @return 文件扩展名(不包含点号),如果无扩展名返回空字符串
|
|
||||||
*/
|
|
||||||
|
|
||||||
fun getFileExtension(fileName: String): String {
|
|
||||||
if (fileName.isEmpty()) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
val lastDotIndex = fileName!!.lastIndexOf('.')
|
|
||||||
return if (lastDotIndex == -1 || lastDotIndex == fileName.length - 1) {
|
|
||||||
""
|
|
||||||
} else fileName.substring(lastDotIndex + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取不带扩展名的文件名
|
|
||||||
*
|
|
||||||
* @param fileName 文件名
|
|
||||||
* @return 不带扩展名的文件名
|
|
||||||
*/
|
|
||||||
fun getFileNameWithoutExtension(fileName: String): String {
|
|
||||||
if (fileName.isEmpty()) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
val lastDotIndex = fileName!!.lastIndexOf('.')
|
|
||||||
return if (lastDotIndex == -1) {
|
|
||||||
fileName
|
|
||||||
} else fileName.substring(0, lastDotIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取文件内容为字节数组
|
* 读取文件内容为字节数组
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @return 文件内容的字节数组表示
|
||||||
* @return 文件内容的字节数组
|
|
||||||
* @throws IOException 读取文件时发生错误
|
* @throws IOException 读取文件时发生错误
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
fun String.readByteArray(): ByteArray {
|
||||||
fun readFileToByteArray(filePath: String): ByteArray {
|
val path = Paths.get(this)
|
||||||
val path = Paths.get(filePath)
|
|
||||||
return Files.readAllBytes(path)
|
return Files.readAllBytes(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将字节数组写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字节数组写入文件
|
* 将字节数组写入文件
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param data 要写入的字节数组
|
||||||
* @param data 要写入的字节数据
|
|
||||||
* @throws IOException 写入文件时发生错误
|
* @throws IOException 写入文件时发生错误
|
||||||
*/
|
*/
|
||||||
|
fun String.writeByteArray(data: ByteArray) {
|
||||||
@Throws(IOException::class)
|
val path = Paths.get(this)
|
||||||
fun writeByteArrayToFile(filePath: String, data: ByteArray) {
|
|
||||||
val path = Paths.get(filePath)
|
|
||||||
Files.createDirectories(path.parent)
|
Files.createDirectories(path.parent)
|
||||||
Files.write(path, data)
|
Files.write(path, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将字节数组追加到文件末尾
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字节数组追加到文件末尾
|
* 将字节数组追加到文件末尾
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param data 要追加的字节数组
|
||||||
* @param data 要追加的字节数据
|
* @throws IOException 追加文件时发生错误
|
||||||
* @throws IOException 追加数据时发生错误
|
|
||||||
*/
|
*/
|
||||||
|
fun String.appendByteArray(data: ByteArray) {
|
||||||
@Throws(IOException::class)
|
val path = Paths.get(this)
|
||||||
fun appendByteArrayToFile(filePath: String, data: ByteArray) {
|
|
||||||
val path = Paths.get(filePath)
|
|
||||||
Files.createDirectories(path.parent)
|
Files.createDirectories(path.parent)
|
||||||
Files.write(
|
Files.write(
|
||||||
path, data, StandardOpenOption.CREATE, StandardOpenOption.APPEND
|
path, data, StandardOpenOption.CREATE, StandardOpenOption.APPEND
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分块读取大文件为字节数组列表
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 分块读取大文件为字节数组列表
|
* 分块读取大文件为字节数组列表
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
* @param chunkSize 每个块的大小
|
||||||
* @param chunkSize 每块大小(字节)
|
* @return 字节数组列表
|
||||||
* @return 文件内容按指定大小分割的字节数组列表
|
|
||||||
* @throws IOException 读取文件时发生错误
|
* @throws IOException 读取文件时发生错误
|
||||||
*/
|
*/
|
||||||
|
fun String.readByteArrayChunks(chunkSize: Int): List<ByteArray> {
|
||||||
@Throws(IOException::class)
|
|
||||||
fun readFileToByteArrayChunks(filePath: String, chunkSize: Int): List<ByteArray> {
|
|
||||||
val chunks = mutableListOf<ByteArray>()
|
val chunks = mutableListOf<ByteArray>()
|
||||||
val path = Paths.get(filePath)
|
val path = Paths.get(this)
|
||||||
|
|
||||||
Files.newInputStream(path).use { inputStream ->
|
Files.newInputStream(path).use { inputStream ->
|
||||||
val buffer = ByteArray(chunkSize)
|
val buffer = ByteArray(chunkSize)
|
||||||
@ -325,17 +282,16 @@ fun readFileToByteArrayChunks(filePath: String, chunkSize: Int): List<ByteArray>
|
|||||||
return chunks
|
return chunks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将字节数组列表写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
/**
|
/**
|
||||||
* 将字节数组列表写入文件
|
* 将字节数组列表写入文件
|
||||||
*
|
*
|
||||||
* @param filePath 文件路径
|
|
||||||
* @param chunks 字节数组列表
|
* @param chunks 字节数组列表
|
||||||
* @throws IOException 写入文件时发生错误
|
* @throws IOException 写入文件时发生错误
|
||||||
*/
|
*/
|
||||||
|
fun String.writeByteArrayChunks(chunks: List<ByteArray>) {
|
||||||
@Throws(IOException::class)
|
val path = Paths.get(this)
|
||||||
fun writeByteArrayChunksToFile(filePath: String, chunks: List<ByteArray>) {
|
|
||||||
val path = Paths.get(filePath)
|
|
||||||
Files.createDirectories(path.parent)
|
Files.createDirectories(path.parent)
|
||||||
|
|
||||||
Files.newOutputStream(path).use { outputStream ->
|
Files.newOutputStream(path).use { outputStream ->
|
||||||
@ -344,3 +300,466 @@ fun writeByteArrayChunksToFile(filePath: String, chunks: List<ByteArray>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字符串
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串,使用默认字符集
|
||||||
|
*
|
||||||
|
* @return 文件内容的字符串表示
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.readFileToString(): String {
|
||||||
|
return this.readFileToString(DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串
|
||||||
|
*
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @return 文件内容的字符串表示
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.readFileToString(charset: Charset): String {
|
||||||
|
val bytes = Files.readAllBytes(this)
|
||||||
|
return String(bytes, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字符串写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串写入文件,使用默认字符集
|
||||||
|
*
|
||||||
|
* @param content 要写入的字符串内容
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.writeStringToFile(content: String) {
|
||||||
|
this.writeStringToFile(content, DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串写入文件
|
||||||
|
*
|
||||||
|
* @param content 要写入的字符串内容
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.writeStringToFile(content: String, charset: Charset) {
|
||||||
|
this.parent?.let { Files.createDirectories(it) }
|
||||||
|
Files.write(this, content.toByteArray(charset))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字符串列表
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串列表,使用默认字符集
|
||||||
|
*
|
||||||
|
* @return 按行分割的字符串列表
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.readLines(): List<String> {
|
||||||
|
return this.readLines(DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串列表
|
||||||
|
*
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @return 按行分割的字符串列表
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.readLines(charset: Charset): List<String> {
|
||||||
|
return Files.readAllLines(this, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字符串列表写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串列表写入文件,使用默认字符集
|
||||||
|
*
|
||||||
|
* @param lines 要写入的字符串列表
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.writeLines(lines: List<String>) {
|
||||||
|
this.writeLines(lines, DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串列表写入文件
|
||||||
|
*
|
||||||
|
* @param lines 要写入的字符串列表
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.writeLines(lines: List<String>, charset: Charset) {
|
||||||
|
Files.createDirectories(this.parent)
|
||||||
|
Files.write(this, lines, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 复制文件到目标路径
|
||||||
|
*
|
||||||
|
* @param target 目标文件路径
|
||||||
|
* @throws IOException 复制文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.copyTo(target: Path) {
|
||||||
|
Files.createDirectories(target.parent)
|
||||||
|
Files.copy(this, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*
|
||||||
|
* @return 删除操作是否成功
|
||||||
|
*/
|
||||||
|
fun Path.delete(): Boolean {
|
||||||
|
return try {
|
||||||
|
Files.deleteIfExists(this)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
/**
|
||||||
|
* 检查文件是否存在
|
||||||
|
*
|
||||||
|
* @return 文件是否存在
|
||||||
|
*/
|
||||||
|
fun Path.exists(): Boolean {
|
||||||
|
return Files.exists(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件大小
|
||||||
|
/**
|
||||||
|
* 获取文件大小
|
||||||
|
*
|
||||||
|
* @return 文件大小(字节),如果获取失败则返回-1
|
||||||
|
*/
|
||||||
|
fun Path.getFileSize(): Long {
|
||||||
|
return try {
|
||||||
|
Files.size(this)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字节数组
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字节数组
|
||||||
|
*
|
||||||
|
* @return 文件内容的字节数组表示
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.readByteArray(): ByteArray {
|
||||||
|
return Files.readAllBytes(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字节数组写入文件
|
||||||
|
*
|
||||||
|
* @param data 要写入的字节数组
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.writeByteArray(data: ByteArray) {
|
||||||
|
Files.createDirectories(this.parent)
|
||||||
|
Files.write(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组追加到文件末尾
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字节数组追加到文件末尾
|
||||||
|
*
|
||||||
|
* @param data 要追加的字节数组
|
||||||
|
* @throws IOException 追加文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.appendByteArray(data: ByteArray) {
|
||||||
|
Files.createDirectories(this.parent)
|
||||||
|
Files.write(this, data, StandardOpenOption.CREATE, StandardOpenOption.APPEND)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分块读取大文件为字节数组列表
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 分块读取大文件为字节数组列表
|
||||||
|
*
|
||||||
|
* @param chunkSize 每个块的大小
|
||||||
|
* @return 字节数组列表
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.readByteArrayChunks(chunkSize: Int): List<ByteArray> {
|
||||||
|
val chunks = mutableListOf<ByteArray>()
|
||||||
|
|
||||||
|
Files.newInputStream(this).use { inputStream ->
|
||||||
|
val buffer = ByteArray(chunkSize)
|
||||||
|
var bytesRead: Int
|
||||||
|
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
val chunk = ByteArray(bytesRead)
|
||||||
|
System.arraycopy(buffer, 0, chunk, 0, bytesRead)
|
||||||
|
chunks.add(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组列表写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字节数组列表写入文件
|
||||||
|
*
|
||||||
|
* @param chunks 字节数组列表
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun Path.writeByteArrayChunks(chunks: List<ByteArray>) {
|
||||||
|
Files.createDirectories(this.parent)
|
||||||
|
|
||||||
|
Files.newOutputStream(this).use { outputStream ->
|
||||||
|
for (chunk in chunks) {
|
||||||
|
outputStream.write(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字符串
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串,使用默认字符集
|
||||||
|
*
|
||||||
|
* @return 文件内容的字符串表示
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.readFileToString(): String {
|
||||||
|
return this.readFileToString(DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串
|
||||||
|
*
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @return 文件内容的字符串表示
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.readFileToString(charset: Charset): String {
|
||||||
|
return String(this.readBytes(), charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字符串写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串写入文件,使用默认字符集
|
||||||
|
*
|
||||||
|
* @param content 要写入的字符串内容
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.writeStringToFile(content: String) {
|
||||||
|
this.writeStringToFile(content, DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串写入文件
|
||||||
|
*
|
||||||
|
* @param content 要写入的字符串内容
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.writeStringToFile(content: String, charset: Charset) {
|
||||||
|
this.parentFile?.mkdirs()
|
||||||
|
this.writeText(content, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字符串列表
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串列表,使用默认字符集
|
||||||
|
*
|
||||||
|
* @return 按行分割的字符串列表
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.readLines(): List<String> {
|
||||||
|
return this.readLines(DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字符串列表
|
||||||
|
*
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @return 按行分割的字符串列表
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.readLines(charset: Charset): List<String> {
|
||||||
|
return this.readLines(charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字符串列表写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串列表写入文件,使用默认字符集
|
||||||
|
*
|
||||||
|
* @param lines 要写入的字符串列表
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.writeLines(lines: List<String>) {
|
||||||
|
this.writeLines(lines, DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字符串列表写入文件
|
||||||
|
*
|
||||||
|
* @param lines 要写入的字符串列表
|
||||||
|
* @param charset 字符编码格式
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.writeLines(lines: List<String>, charset: Charset) {
|
||||||
|
this.parentFile?.mkdirs()
|
||||||
|
this.writeLines(lines, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 复制文件到目标文件
|
||||||
|
*
|
||||||
|
* @param target 目标文件
|
||||||
|
* @throws IOException 复制文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.copyTo(target: File) {
|
||||||
|
target.parentFile?.mkdirs()
|
||||||
|
this.copyTo(target, overwrite = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*
|
||||||
|
* @return 删除操作是否成功
|
||||||
|
*/
|
||||||
|
fun File.delete(): Boolean {
|
||||||
|
return this.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
/**
|
||||||
|
* 检查文件是否存在
|
||||||
|
*
|
||||||
|
* @return 文件是否存在
|
||||||
|
*/
|
||||||
|
fun File.exists(): Boolean {
|
||||||
|
return this.exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件大小
|
||||||
|
/**
|
||||||
|
* 获取文件大小
|
||||||
|
*
|
||||||
|
* @return 文件大小(字节),如果文件不存在则返回-1L
|
||||||
|
*/
|
||||||
|
fun File.getFileSize(): Long {
|
||||||
|
return if (this.exists()) this.length() else -1L
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建目录
|
||||||
|
/**
|
||||||
|
* 创建目录
|
||||||
|
*
|
||||||
|
* @return 创建操作是否成功
|
||||||
|
*/
|
||||||
|
fun File.createDirectory(): Boolean {
|
||||||
|
return this.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容为字节数组
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 读取文件内容为字节数组
|
||||||
|
*
|
||||||
|
* @return 文件内容的字节数组表示
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.readByteArray(): ByteArray {
|
||||||
|
return this.readBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字节数组写入文件
|
||||||
|
*
|
||||||
|
* @param data 要写入的字节数组
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.writeByteArray(data: ByteArray) {
|
||||||
|
this.parentFile?.mkdirs()
|
||||||
|
this.writeBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组追加到文件末尾
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字节数组追加到文件末尾
|
||||||
|
*
|
||||||
|
* @param data 要追加的字节数组
|
||||||
|
* @throws IOException 追加文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.appendByteArray(data: ByteArray) {
|
||||||
|
this.parentFile?.mkdirs()
|
||||||
|
this.appendBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分块读取大文件为字节数组列表
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 分块读取大文件为字节数组列表
|
||||||
|
*
|
||||||
|
* @param chunkSize 每个块的大小
|
||||||
|
* @return 字节数组列表
|
||||||
|
* @throws IOException 读取文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.readByteArrayChunks(chunkSize: Int): List<ByteArray> {
|
||||||
|
val chunks = mutableListOf<ByteArray>()
|
||||||
|
|
||||||
|
this.inputStream().use { inputStream ->
|
||||||
|
val buffer = ByteArray(chunkSize)
|
||||||
|
var bytesRead: Int
|
||||||
|
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
val chunk = ByteArray(bytesRead)
|
||||||
|
System.arraycopy(buffer, 0, chunk, 0, bytesRead)
|
||||||
|
chunks.add(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组列表写入文件
|
||||||
|
@Throws(IOException::class)
|
||||||
|
/**
|
||||||
|
* 将字节数组列表写入文件
|
||||||
|
*
|
||||||
|
* @param chunks 字节数组列表
|
||||||
|
* @throws IOException 写入文件时发生错误
|
||||||
|
*/
|
||||||
|
fun File.writeByteArrayChunks(chunks: List<ByteArray>) {
|
||||||
|
this.parentFile?.mkdirs()
|
||||||
|
|
||||||
|
this.outputStream().use { outputStream ->
|
||||||
|
for (chunk in chunks) {
|
||||||
|
outputStream.write(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
81
src/main/kotlin/com/mingliqiye/utils/functions/Debouncer.kt
Normal file
81
src/main/kotlin/com/mingliqiye/utils/functions/Debouncer.kt
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 mingliqiye
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* ProjectName mingli-utils
|
||||||
|
* ModuleName mingli-utils.main
|
||||||
|
* CurrentFile Debouncer.kt
|
||||||
|
* LastUpdate 2026-01-11 09:10:30
|
||||||
|
* UpdateUser MingLiPro
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.mingliqiye.utils.functions
|
||||||
|
|
||||||
|
import java.util.concurrent.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防抖器类,用于实现防抖功能,防止在短时间内重复执行相同任务
|
||||||
|
*
|
||||||
|
* @author MingLiPro
|
||||||
|
*/
|
||||||
|
class Debouncer(private val delay: Long, unit: TimeUnit) {
|
||||||
|
|
||||||
|
private val scheduler: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
private val delayedMap: ConcurrentHashMap<Any, Future<*>> = ConcurrentHashMap()
|
||||||
|
private val delayMillis: Long = unit.toMillis(delay)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行防抖操作,如果在指定延迟时间内再次调用相同key的任务,则取消之前的任务并重新计时
|
||||||
|
*
|
||||||
|
* @param key 任务的唯一标识符,用于区分不同任务
|
||||||
|
* @param task 要执行的任务
|
||||||
|
*/
|
||||||
|
fun debounce(key: Any, task: Runnable) {
|
||||||
|
// 提交新任务并获取之前可能存在的任务
|
||||||
|
val prev = delayedMap.put(
|
||||||
|
key,
|
||||||
|
scheduler.schedule(
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
task.run()
|
||||||
|
} finally {
|
||||||
|
// 任务执行完成后从映射中移除
|
||||||
|
delayedMap.remove(key)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delayMillis,
|
||||||
|
TimeUnit.MILLISECONDS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 如果之前存在任务,则取消它
|
||||||
|
if (prev != null) {
|
||||||
|
prev.cancel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭防抖器,取消所有待执行的任务并关闭调度器
|
||||||
|
*/
|
||||||
|
fun shutdown() {
|
||||||
|
// 先取消所有延迟任务
|
||||||
|
for (future in delayedMap.values) {
|
||||||
|
future.cancel(true)
|
||||||
|
}
|
||||||
|
delayedMap.clear()
|
||||||
|
|
||||||
|
// 再关闭调度器
|
||||||
|
scheduler.shutdownNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,70 +16,14 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile Functions.kt
|
* CurrentFile Functions.kt
|
||||||
* LastUpdate 2026-01-09 08:12:01
|
* LastUpdate 2026-01-11 09:10:48
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:JvmName("Functions")
|
||||||
|
|
||||||
package com.mingliqiye.utils.functions
|
package com.mingliqiye.utils.functions
|
||||||
|
|
||||||
import java.util.concurrent.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 防抖器类,用于实现防抖功能,防止在短时间内重复执行相同任务
|
|
||||||
*
|
|
||||||
* @author MingLiPro
|
|
||||||
*/
|
|
||||||
class Debouncer(private val delay: Long, unit: TimeUnit) {
|
|
||||||
|
|
||||||
private val scheduler: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
|
|
||||||
private val delayedMap: ConcurrentHashMap<Any, Future<*>> = ConcurrentHashMap()
|
|
||||||
private val delayMillis: Long = unit.toMillis(delay)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行防抖操作,如果在指定延迟时间内再次调用相同key的任务,则取消之前的任务并重新计时
|
|
||||||
*
|
|
||||||
* @param key 任务的唯一标识符,用于区分不同任务
|
|
||||||
* @param task 要执行的任务
|
|
||||||
*/
|
|
||||||
fun debounce(key: Any, task: Runnable) {
|
|
||||||
// 提交新任务并获取之前可能存在的任务
|
|
||||||
val prev = delayedMap.put(
|
|
||||||
key,
|
|
||||||
scheduler.schedule(
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
task.run()
|
|
||||||
} finally {
|
|
||||||
// 任务执行完成后从映射中移除
|
|
||||||
delayedMap.remove(key)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
delayMillis,
|
|
||||||
TimeUnit.MILLISECONDS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// 如果之前存在任务,则取消它
|
|
||||||
if (prev != null) {
|
|
||||||
prev.cancel(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭防抖器,取消所有待执行的任务并关闭调度器
|
|
||||||
*/
|
|
||||||
fun shutdown() {
|
|
||||||
// 先取消所有延迟任务
|
|
||||||
for (future in delayedMap.values) {
|
|
||||||
future.cancel(true)
|
|
||||||
}
|
|
||||||
delayedMap.clear()
|
|
||||||
|
|
||||||
// 再关闭调度器
|
|
||||||
scheduler.shutdownNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
fun interface P1Function<P> {
|
fun interface P1Function<P> {
|
||||||
fun call(p: P)
|
fun call(p: P)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2025 mingliqiye
|
* Copyright 2026 mingliqiye
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,7 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile HashUtils.kt
|
* CurrentFile HashUtils.kt
|
||||||
* LastUpdate 2025-09-19 20:24:33
|
* LastUpdate 2026-01-11 09:09:52
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
@file:JvmName("HashUtils")
|
@file:JvmName("HashUtils")
|
||||||
@ -24,9 +24,9 @@
|
|||||||
package com.mingliqiye.utils.hash
|
package com.mingliqiye.utils.hash
|
||||||
|
|
||||||
|
|
||||||
import com.mingliqiye.utils.base.BASE16
|
|
||||||
import com.mingliqiye.utils.bcrypt.checkpw
|
import com.mingliqiye.utils.bcrypt.checkpw
|
||||||
import com.mingliqiye.utils.bcrypt.hashpw
|
import com.mingliqiye.utils.bcrypt.hashpw
|
||||||
|
import com.mingliqiye.utils.bytes.bytesToHex
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -64,16 +64,6 @@ fun calculateFileHash(file: File, algorithm: String): String {
|
|||||||
return bytesToHex(digest.digest())
|
return bytesToHex(digest.digest())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 将字节数组转换为十六进制字符串表示。
|
|
||||||
*
|
|
||||||
* @param bytes 输入的字节数组
|
|
||||||
* @return 对应的十六进制字符串
|
|
||||||
*/
|
|
||||||
private fun bytesToHex(bytes: ByteArray): String {
|
|
||||||
return BASE16.encode(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用 BCrypt 算法对字符串进行加密。
|
* 使用 BCrypt 算法对字符串进行加密。
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2025 mingliqiye
|
* Copyright 2026 mingliqiye
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,13 +16,16 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile QueryWrapper.kt
|
* CurrentFile QueryWrapper.kt
|
||||||
* LastUpdate 2025-09-20 14:21:44
|
* LastUpdate 2026-01-14 13:00:31
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.mingliqiye.utils.mybatisplus
|
package com.mingliqiye.utils.mybatisplus
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,4 +42,31 @@ interface BaseMapperQuery<T> : BaseMapper<T> {
|
|||||||
fun queryWrapper(): QueryWrapper<T> {
|
fun queryWrapper(): QueryWrapper<T> {
|
||||||
return QueryWrapper<T>()
|
return QueryWrapper<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个新的UpdateWrapper实例
|
||||||
|
*
|
||||||
|
* @return UpdateWrapper<T> 返回类型化的更新包装器实例
|
||||||
|
*/
|
||||||
|
fun updateWrapper(): UpdateWrapper<T> {
|
||||||
|
return UpdateWrapper<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个新的LambdaQueryWrapper实例
|
||||||
|
*
|
||||||
|
* @return LambdaQueryWrapper<T> 返回类型化的Lambda查询包装器实例
|
||||||
|
*/
|
||||||
|
fun lambdaQueryWrapper(): LambdaQueryWrapper<T> {
|
||||||
|
return LambdaQueryWrapper<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个新的LambdaUpdateWrapper实例
|
||||||
|
*
|
||||||
|
* @return LambdaUpdateWrapper<T> 返回类型化的Lambda更新包装器实例
|
||||||
|
*/
|
||||||
|
fun lambdaUpdateWrapper(): LambdaUpdateWrapper<T> {
|
||||||
|
return LambdaUpdateWrapper<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
* ProjectName mingli-utils
|
* ProjectName mingli-utils
|
||||||
* ModuleName mingli-utils.main
|
* ModuleName mingli-utils.main
|
||||||
* CurrentFile Require.kt
|
* CurrentFile Require.kt
|
||||||
* LastUpdate 2026-01-10 08:53:26
|
* LastUpdate 2026-01-10 09:01:03
|
||||||
* UpdateUser MingLiPro
|
* UpdateUser MingLiPro
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -25,43 +25,22 @@ package com.mingliqiye.utils.request
|
|||||||
import com.mingliqiye.utils.functions.P1RFunction
|
import com.mingliqiye.utils.functions.P1RFunction
|
||||||
import com.mingliqiye.utils.functions.RFunction
|
import com.mingliqiye.utils.functions.RFunction
|
||||||
|
|
||||||
class Require(private val must: Boolean) {
|
/**
|
||||||
|
* 扩展函数:基于布尔值创建Require对象,并指定异常消息和异常构造器
|
||||||
constructor(funs: RFunction<Boolean>) : this(funs.call())
|
* @param message 异常消息
|
||||||
|
* @param exception 异常构造器函数
|
||||||
constructor(must: RFunction<Boolean>, message: String) : this(must) {
|
* @return Require对象
|
||||||
throws(message)
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
must: RFunction<Boolean>,
|
|
||||||
message: String,
|
|
||||||
exception: Class<out Exception> = IllegalArgumentException::class.java
|
|
||||||
) : this(must) {
|
|
||||||
throws(message, exception)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(must: Boolean, message: String) : this(must) {
|
|
||||||
throws(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
must: Boolean, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
|
|
||||||
) : this(must) {
|
|
||||||
throws(message, exception)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
must: Boolean, message: String, exception: P1RFunction<String, out Exception>
|
|
||||||
) : this(must) {
|
|
||||||
throws(message, exception)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun Boolean.require(message: String, exception: P1RFunction<String, out Exception>): Require {
|
fun Boolean.require(message: String, exception: P1RFunction<String, out Exception>): Require {
|
||||||
return Require(this, message, exception)
|
return Require(this, message, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展函数:基于布尔值创建Require对象,并指定异常消息和异常类型
|
||||||
|
* @param message 异常消息
|
||||||
|
* @param exception 异常类型,默认为IllegalArgumentException
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
fun Boolean.require(
|
fun Boolean.require(
|
||||||
message: String,
|
message: String,
|
||||||
exception: Class<out Exception> = IllegalArgumentException::class.java
|
exception: Class<out Exception> = IllegalArgumentException::class.java
|
||||||
@ -69,6 +48,83 @@ class Require(private val must: Boolean) {
|
|||||||
return Require(this, message, exception)
|
return Require(this, message, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件检查工具类,用于验证条件并抛出相应异常
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
*/
|
||||||
|
class Require(private val must: Boolean) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:通过函数调用结果初始化条件检查器
|
||||||
|
* @param funs 返回布尔值的函数
|
||||||
|
*/
|
||||||
|
constructor(funs: RFunction<Boolean>) : this(funs.call())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:通过函数调用结果初始化条件检查器,并立即执行检查
|
||||||
|
* @param must 返回布尔值的函数
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
*/
|
||||||
|
constructor(must: RFunction<Boolean>, message: String) : this(must) {
|
||||||
|
throws(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:通过函数调用结果初始化条件检查器,并立即执行检查
|
||||||
|
* @param must 返回布尔值的函数
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常类型,默认为IllegalArgumentException
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
must: RFunction<Boolean>,
|
||||||
|
message: String,
|
||||||
|
exception: Class<out Exception> = IllegalArgumentException::class.java
|
||||||
|
) : this(must) {
|
||||||
|
throws(message, exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:通过布尔值初始化条件检查器,并立即执行检查
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
*/
|
||||||
|
constructor(must: Boolean, message: String) : this(must) {
|
||||||
|
throws(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:通过布尔值初始化条件检查器,并立即执行检查
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常类型,默认为IllegalArgumentException
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
must: Boolean, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
|
||||||
|
) : this(must) {
|
||||||
|
throws(message, exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数:通过布尔值初始化条件检查器,并立即执行检查
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常构造器函数
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
must: Boolean, message: String, exception: P1RFunction<String, out Exception>
|
||||||
|
) : this(must) {
|
||||||
|
throws(message, exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工厂方法:创建Require对象并指定异常消息和异常类型
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常类型,默认为IllegalArgumentException
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun require(
|
fun require(
|
||||||
must: Boolean, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
|
must: Boolean, message: String, exception: Class<out Exception> = IllegalArgumentException::class.java
|
||||||
@ -76,16 +132,34 @@ class Require(private val must: Boolean) {
|
|||||||
return Require(must, message, exception)
|
return Require(must, message, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工厂方法:创建Require对象并指定异常消息
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun require(must: Boolean, message: String): Require {
|
fun require(must: Boolean, message: String): Require {
|
||||||
return Require(must, message)
|
return Require(must, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工厂方法:创建Require对象
|
||||||
|
* @param must 需要验证的布尔条件
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun require(must: Boolean): Require {
|
fun require(must: Boolean): Require {
|
||||||
return Require(must)
|
return Require(must)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工厂方法:创建Require对象并指定异常消息和异常类型
|
||||||
|
* @param must 返回布尔值的函数
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常类型,默认为IllegalArgumentException
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun require(
|
fun require(
|
||||||
must: RFunction<Boolean>,
|
must: RFunction<Boolean>,
|
||||||
@ -95,29 +169,54 @@ class Require(private val must: Boolean) {
|
|||||||
return Require(must, message, exception)
|
return Require(must, message, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工厂方法:创建Require对象并指定异常消息
|
||||||
|
* @param must 返回布尔值的函数
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun require(must: RFunction<Boolean>, message: String): Require {
|
fun require(must: RFunction<Boolean>, message: String): Require {
|
||||||
return Require(must, message)
|
return Require(must, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工厂方法:创建Require对象
|
||||||
|
* @param must 返回布尔值的函数
|
||||||
|
* @return Require对象
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun require(must: RFunction<Boolean>): Require {
|
fun require(must: RFunction<Boolean>): Require {
|
||||||
return Require(must)
|
return Require(must)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行条件检查,如果条件为false则抛出IllegalArgumentException
|
||||||
|
* @param message 检查失败时的异常消息
|
||||||
|
*/
|
||||||
fun throws(message: String) {
|
fun throws(message: String) {
|
||||||
if (!must) {
|
if (!must) {
|
||||||
throw IllegalArgumentException(message)
|
throw IllegalArgumentException(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行条件检查,如果条件为false则抛出指定类型的异常
|
||||||
|
* @param string 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常类型
|
||||||
|
*/
|
||||||
fun throws(string: String, exception: Class<out Exception>) {
|
fun throws(string: String, exception: Class<out Exception>) {
|
||||||
if (!must) {
|
if (!must) {
|
||||||
throw exception.getConstructor(String::class.java).newInstance(string)
|
throw exception.getConstructor(String::class.java).newInstance(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行条件检查,如果条件为false则抛出由函数构造的异常
|
||||||
|
* @param string 检查失败时的异常消息
|
||||||
|
* @param exception 检查失败时抛出的异常构造器函数
|
||||||
|
*/
|
||||||
fun throws(string: String, exception: P1RFunction<String, out Exception>) {
|
fun throws(string: String, exception: P1RFunction<String, out Exception>) {
|
||||||
if (!must) {
|
if (!must) {
|
||||||
throw exception.call(string)
|
throw exception.call(string)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user