Browse Source

add dataSize

Looly 5 years ago
parent
commit
d70687d20d

+ 2 - 1
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)

+ 3 - 7
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);
 	}
 
 	/**

+ 277 - 0
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长度的数字
+ * <p>
+ * 此类来自于:Spring-framework
+ *
+ * <p>
+ * <table border="1">
+ * <tr><th>Term</th><th>Data Size</th><th>Size in Bytes</th></tr>
+ * <tr><td>byte</td><td>1B</td><td>1</td></tr>
+ * <tr><td>kilobyte</td><td>1KB</td><td>1,024</td></tr>
+ * <tr><td>megabyte</td><td>1MB</td><td>1,048,576</td></tr>
+ * <tr><td>gigabyte</td><td>1GB</td><td>1,073,741,824</td></tr>
+ * <tr><td>terabyte</td><td>1TB</td><td>1,099,511,627,776</td></tr>
+ * </table>
+ *
+ * @author Sam Brannen,Stephane Nicoll
+ * @since 5.3.10
+ */
+public final class DataSize implements Comparable<DataSize> {
+
+	/**
+	 * 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}
+	 * <p>
+	 * 例如:
+	 * <pre>
+	 * "12KB" -- parses as "12 kilobytes"
+	 * "5MB"  -- parses as "5 megabytes"
+	 * "20"   -- parses as "20 bytes"
+	 * </pre>
+	 *
+	 * @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.
+	 * <p>
+	 * The string starts with a number followed optionally by a unit matching one of the
+	 * supported {@linkplain DataUnit suffixes}.
+	 * <p>
+	 * Examples:
+	 * <pre>
+	 * "12KB" -- parses as "12 kilobytes"
+	 * "5MB"  -- parses as "5 megabytes"
+	 * "20"   -- parses as "20 kilobytes" (where the {@code defaultUnit} is {@link DataUnit#KILOBYTES})
+	 * </pre>
+	 *
+	 * @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);
+	}
+
+}

+ 38 - 0
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();
+	}
+
+	/**
+	 * 可读的文件大小<br>
+	 * 参考 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];
+	}
+}

+ 79 - 0
hutool-core/src/main/java/cn/hutool/core/io/unit/DataUnit.java

@@ -0,0 +1,79 @@
+package cn.hutool.core.io.unit;
+
+/**
+ * 数据单位封装<p>
+ * 此类来自于:Spring-framework
+ *
+ * <p>
+ * <table border="1">
+ * <tr><th>名称</th><th>数据大小</th><th>Power&nbsp;of&nbsp;2</th><th>bytes表示</th></tr>
+ * <tr><td>{@link #BYTES}</td><td>1B</td><td>2^0</td><td>1</td></tr>
+ * <tr><td>{@link #KILOBYTES}</td><td>1KB</td><td>2^10</td><td>1,024</td></tr>
+ * <tr><td>{@link #MEGABYTES}</td><td>1MB</td><td>2^20</td><td>1,048,576</td></tr>
+ * <tr><td>{@link #GIGABYTES}</td><td>1GB</td><td>2^30</td><td>1,073,741,824</td></tr>
+ * <tr><td>{@link #TERABYTES}</td><td>1TB</td><td>2^40</td><td>1,099,511,627,776</td></tr>
+ * </table>
+ *
+ * @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 + "'");
+	}
+
+}

+ 7 - 0
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;