diff --git a/CHANGELOG.md b/CHANGELOG.md index c5b72a602..61e0d2f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,11 @@ ------------------------------------------------------------------------------------------------------------- -## 5.3.10 (2020-07-14) +## 5.3.10 (2020-07-16) ### 新特性 * 【db 】 增加DbUtil.setReturnGeneratedKeyGlobal(issue#I1NM0K@Gitee) +* 【core 】 增加DataSize和DataSizeUtil(issue#967@Github) ### Bug修复 * 【core 】 修复ZipUtil中finish位于循环内的问题(issue#961@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index 79a5313b0..310c74ef6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -9,6 +9,7 @@ import cn.hutool.core.io.file.FileWriter; import cn.hutool.core.io.file.LineSeparator; import cn.hutool.core.io.file.Tailer; import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.io.unit.DataSizeUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; @@ -50,7 +51,6 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -3361,14 +3361,10 @@ public class FileUtil { * * @param size Long类型大小 * @return 大小 + * @see DataSizeUtil#format(long) */ public static String readableFileSize(long size) { - if (size <= 0) { - return "0"; - } - final String[] units = new String[]{"B", "kB", "MB", "GB", "TB", "EB"}; - int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); - return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; + return DataSizeUtil.format(size); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java new file mode 100644 index 000000000..da01c45a2 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSize.java @@ -0,0 +1,277 @@ +package cn.hutool.core.io.unit; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 数据大小,可以将类似于'12MB'表示转换为bytes长度的数字 + *

+ * 此类来自于:Spring-framework + * + *

+ * + * + * + * + * + * + * + *
TermData SizeSize in Bytes
byte1B1
kilobyte1KB1,024
megabyte1MB1,048,576
gigabyte1GB1,073,741,824
terabyte1TB1,099,511,627,776
+ * + * @author Sam Brannen,Stephane Nicoll + * @since 5.3.10 + */ +public final class DataSize implements Comparable { + + /** + * The pattern for parsing. + */ + private static final Pattern PATTERN = Pattern.compile("^([+\\-]?\\d+)([a-zA-Z]{0,2})$"); + + /** + * Bytes per Kilobyte(KB). + */ + private static final long BYTES_PER_KB = 1024; + + /** + * Bytes per Megabyte(MB). + */ + private static final long BYTES_PER_MB = BYTES_PER_KB * 1024; + + /** + * Bytes per Gigabyte(GB). + */ + private static final long BYTES_PER_GB = BYTES_PER_MB * 1024; + + /** + * Bytes per Terabyte(TB). + */ + private static final long BYTES_PER_TB = BYTES_PER_GB * 1024; + + + /** + * bytes长度 + */ + private final long bytes; + + + /** + * 构造 + * + * @param bytes 长度 + */ + private DataSize(long bytes) { + this.bytes = bytes; + } + + + /** + * 获得对应bytes的{@link DataSize} + * + * @param bytes bytes大小,可正可负 + * @return a {@link DataSize} + */ + public static DataSize ofBytes(long bytes) { + return new DataSize(bytes); + } + + /** + * 获得对应kilobytes的{@link DataSize} + * + * @param kilobytes kilobytes大小,可正可负 + * @return a {@link DataSize} + */ + public static DataSize ofKilobytes(long kilobytes) { + return new DataSize(Math.multiplyExact(kilobytes, BYTES_PER_KB)); + } + + /** + * 获得对应megabytes的{@link DataSize} + * + * @param megabytes megabytes大小,可正可负 + * @return a {@link DataSize} + */ + public static DataSize ofMegabytes(long megabytes) { + return new DataSize(Math.multiplyExact(megabytes, BYTES_PER_MB)); + } + + /** + * 获得对应gigabytes的{@link DataSize} + * + * @param gigabytes gigabytes大小,可正可负 + * @return a {@link DataSize} + */ + public static DataSize ofGigabytes(long gigabytes) { + return new DataSize(Math.multiplyExact(gigabytes, BYTES_PER_GB)); + } + + /** + * 获得对应terabytes的{@link DataSize} + * + * @param terabytes terabytes大小,可正可负 + * @return a {@link DataSize} + */ + public static DataSize ofTerabytes(long terabytes) { + return new DataSize(Math.multiplyExact(terabytes, BYTES_PER_TB)); + } + + /** + * 获得指定{@link DataUnit}对应的{@link DataSize} + * + * @param amount 大小 + * @param unit 数据大小单位,null表示默认的BYTES + * @return {@link DataSize} + */ + public static DataSize of(long amount, DataUnit unit) { + if(null == unit){ + unit = DataUnit.BYTES; + } + return new DataSize(Math.multiplyExact(amount, unit.size().toBytes())); + } + + /** + * 获取指定数据大小文本对应的{@link DataSize}对象,如果无单位指定,默认获取{@link DataUnit#BYTES} + *

+ * 例如: + *

+	 * "12KB" -- parses as "12 kilobytes"
+	 * "5MB"  -- parses as "5 megabytes"
+	 * "20"   -- parses as "20 bytes"
+	 * 
+ * + * @param text the text to parse + * @return the parsed {@link DataSize} + * @see #parse(CharSequence, DataUnit) + */ + public static DataSize parse(CharSequence text) { + return parse(text, null); + } + + /** + * Obtain a {@link DataSize} from a text string such as {@code 12MB} using + * the specified default {@link DataUnit} if no unit is specified. + *

+ * The string starts with a number followed optionally by a unit matching one of the + * supported {@linkplain DataUnit suffixes}. + *

+ * Examples: + *

+	 * "12KB" -- parses as "12 kilobytes"
+	 * "5MB"  -- parses as "5 megabytes"
+	 * "20"   -- parses as "20 kilobytes" (where the {@code defaultUnit} is {@link DataUnit#KILOBYTES})
+	 * 
+ * + * @param text the text to parse + * @return the parsed {@link DataSize} + */ + public static DataSize parse(CharSequence text, DataUnit defaultUnit) { + Assert.notNull(text, "Text must not be null"); + try { + Matcher matcher = PATTERN.matcher(text); + Assert.state(matcher.matches(), "Does not match data size pattern"); + DataUnit unit = determineDataUnit(matcher.group(2), defaultUnit); + long amount = Long.parseLong(matcher.group(1)); + return DataSize.of(amount, unit); + } catch (Exception ex) { + throw new IllegalArgumentException("'" + text + "' is not a valid data size", ex); + } + } + + /** + * 决定数据单位,后缀不识别时使用默认单位 + * @param suffix 后缀 + * @param defaultUnit 默认单位 + * @return {@link DataUnit} + */ + private static DataUnit determineDataUnit(String suffix, DataUnit defaultUnit) { + DataUnit defaultUnitToUse = (defaultUnit != null ? defaultUnit : DataUnit.BYTES); + return (StrUtil.isNotEmpty(suffix) ? DataUnit.fromSuffix(suffix) : defaultUnitToUse); + } + + /** + * 是否为负数,不包括0 + * + * @return 负数返回true,否则false + */ + public boolean isNegative() { + return this.bytes < 0; + } + + /** + * 返回bytes大小 + * + * @return bytes大小 + */ + public long toBytes() { + return this.bytes; + } + + /** + * 返回KB大小 + * + * @return KB大小 + */ + public long toKilobytes() { + return this.bytes / BYTES_PER_KB; + } + + /** + * 返回MB大小 + * + * @return MB大小 + */ + public long toMegabytes() { + return this.bytes / BYTES_PER_MB; + } + + /** + * 返回GB大小 + * + * @return GB大小 + * + */ + public long toGigabytes() { + return this.bytes / BYTES_PER_GB; + } + + /** + * 返回TB大小 + * + * @return TB大小 + */ + public long toTerabytes() { + return this.bytes / BYTES_PER_TB; + } + + @Override + public int compareTo(DataSize other) { + return Long.compare(this.bytes, other.bytes); + } + + @Override + public String toString() { + return String.format("%dB", this.bytes); + } + + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + DataSize otherSize = (DataSize) other; + return (this.bytes == otherSize.bytes); + } + + @Override + public int hashCode() { + return Long.hashCode(this.bytes); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java new file mode 100644 index 000000000..e38e87d42 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataSizeUtil.java @@ -0,0 +1,38 @@ +package cn.hutool.core.io.unit; + +import java.text.DecimalFormat; + +/** + * 数据大小工具类 + * + * @author looly + * @since 5.3.10 + */ +public class DataSizeUtil { + + /** + * 解析数据大小字符串,转换为bytes大小 + * + * @param text 数据大小字符串,类似于:12KB, 5MB等 + * @return bytes大小 + */ + public long parse(String text) { + return DataSize.parse(text).toBytes(); + } + + /** + * 可读的文件大小
+ * 参考 http://stackoverflow.com/questions/3263892/format-file-size-as-mb-gb-etc + * + * @param size Long类型大小 + * @return 大小 + */ + public static String format(long size) { + if (size <= 0) { + return "0"; + } + int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); + return new DecimalFormat("#,##0.##") + .format(size / Math.pow(1024, digitGroups)) + " " + DataUnit.UNIT_NAMES[digitGroups]; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java new file mode 100644 index 000000000..3a7d1ae53 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java @@ -0,0 +1,79 @@ +package cn.hutool.core.io.unit; + +/** + * 数据单位封装

+ * 此类来自于:Spring-framework + * + *

+ * + * + * + * + * + * + * + *
名称数据大小Power of 2bytes表示
{@link #BYTES}1B2^01
{@link #KILOBYTES}1KB2^101,024
{@link #MEGABYTES}1MB2^201,048,576
{@link #GIGABYTES}1GB2^301,073,741,824
{@link #TERABYTES}1TB2^401,099,511,627,776
+ * + * @author Sam Brannen,Stephane Nicoll + * @since 5.3.10 + */ +public enum DataUnit { + + /** + * Bytes, 后缀表示为: {@code B}. + */ + BYTES("B", DataSize.ofBytes(1)), + + /** + * Kilobytes, 后缀表示为: {@code KB}. + */ + KILOBYTES("KB", DataSize.ofKilobytes(1)), + + /** + * Megabytes, 后缀表示为: {@code MB}. + */ + MEGABYTES("MB", DataSize.ofMegabytes(1)), + + /** + * Gigabytes, 后缀表示为: {@code GB}. + */ + GIGABYTES("GB", DataSize.ofGigabytes(1)), + + /** + * Terabytes, 后缀表示为: {@code TB}. + */ + TERABYTES("TB", DataSize.ofTerabytes(1)); + + public static final String[] UNIT_NAMES = new String[]{"B", "kB", "MB", "GB", "TB", "EB"}; + + private final String suffix; + + private final DataSize size; + + + DataUnit(String suffix, DataSize size) { + this.suffix = suffix; + this.size = size; + } + + DataSize size() { + return this.size; + } + + /** + * 通过后缀返回对应的 {@link DataUnit} + * + * @param suffix 单位后缀 + * @return 匹配到的{@link DataUnit} + * @throws IllegalArgumentException 后缀无法识别报错 + */ + public static DataUnit fromSuffix(String suffix) { + for (DataUnit candidate : values()) { + if (candidate.suffix.equalsIgnoreCase(suffix)) { + return candidate; + } + } + throw new IllegalArgumentException("Unknown data unit suffix '" + suffix + "'"); + } + +} \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/io/unit/package-info.java b/hutool-core/src/main/java/cn/hutool/core/io/unit/package-info.java new file mode 100644 index 000000000..86abbe6c6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/unit/package-info.java @@ -0,0 +1,7 @@ +/** + * 数据单位相关封装,包括DataUnit数据单位和DataSize数据大小 + * + * @author looly + * @since 5.3.10 + */ +package cn.hutool.core.io.unit; \ No newline at end of file