diff --git a/CHANGELOG.md b/CHANGELOG.md index 741ef609f..6ce31bdb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * 【core 】 CharSequenceUtil增加replace重载(issue#2122@Github) * 【core 】 IntMap和LongMap使用位运算快速求解取余运算(pr#2123@Github) * 【core 】 新增通用builder类:GenericBuilder(pr#526@Gitee) +* 【core 】 新增copySafely方法与mkdirsSafely方法(pr#527@Gitee) ### 🐞Bug修复 * 【core 】 修复ChineseDate农历获取正月出现数组越界BUG(issue#2112@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 6fc1f748f..72eb18ca6 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 @@ -527,26 +527,39 @@ public class FileUtil extends PathUtil { /** * 计算目录或文件的总大小
* 当给定对象为文件时,直接调用 {@link File#length()}
- * 当给定对象为目录时,遍历目录下的所有文件和目录,递归计算其大小,求和返回 + * 当给定对象为目录时,遍历目录下的所有文件和目录,递归计算其大小,求和返回
+ * 此方法不包括目录本身的占用空间大小。 * * @param file 目录或文件,null或者文件不存在返回0 * @return 总大小,bytes长度 */ public static long size(File file) { + return size(file, false); + } + + /** + * 计算目录或文件的总大小
+ * 当给定对象为文件时,直接调用 {@link File#length()}
+ * 当给定对象为目录时,遍历目录下的所有文件和目录,递归计算其大小,求和返回 + * + * @param file 目录或文件,null或者文件不存在返回0 + * @param includeDirSize 是否包括每层目录本身的大小 + * @return 总大小,bytes长度 + * @since 5.7.21 + */ + public static long size(File file, boolean includeDirSize) { if (null == file || false == file.exists() || isSymlink(file)) { return 0; } if (file.isDirectory()) { - // TODO: 是否需要统计目录本身的大小呢? - // size += file.length(); - long size = 0L; + long size = includeDirSize ? file.length() : 0; File[] subFiles = file.listFiles(); if (ArrayUtil.isEmpty(subFiles)) { return 0L;// empty directory } for (File subFile : subFiles) { - size += size(subFile); + size += size(subFile, includeDirSize); } return size; } else { @@ -610,7 +623,6 @@ public class FileUtil extends PathUtil { return null; } if (false == file.exists()) { - // TODO: {@see mkdirsSafely} 确保并发环境下的安全创建 mkParentDirs(file); try { //noinspection ResultOfMethodCallIgnored @@ -815,7 +827,7 @@ public class FileUtil extends PathUtil { /** * 创建文件夹,会递归自动创建其不存在的父文件夹,如果存在直接返回此文件夹
- * 此方法不对File对象类型做判断,如果File不存在,无法判断其类型 + * 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
* * @param dir 目录 * @return 创建的目录 @@ -825,8 +837,7 @@ public class FileUtil extends PathUtil { return null; } if (false == dir.exists()) { - //noinspection ResultOfMethodCallIgnored - dir.mkdirs(); + mkdirsSafely(dir, 5, 1); } return dir; } @@ -843,23 +854,27 @@ public class FileUtil extends PathUtil { * * * @param dir 待创建的目录 + * @param tryCount 最大尝试次数 + * @param sleepMillis 线程等待的毫秒数 * @return true表示创建成功,false表示创建失败 - * @since 2022-01-29 + * @since 5.7.21 * @author z8g */ - public static boolean mkdirsSafely(File dir) { + public static boolean mkdirsSafely(File dir, int tryCount, long sleepMillis) { if (dir == null) { return false; } if (dir.isDirectory()) { return true; } - for (int i = 1; i <= 5; i++) { // 高并发场景下,可以看到 i 处于 1 ~ 3 之间 - dir.mkdirs(); // 如果文件已存在,也会返回 false,所以该值不能作为是否能创建的依据,因此不对其进行处理 + for (int i = 1; i <= tryCount; i++) { // 高并发场景下,可以看到 i 处于 1 ~ 3 之间 + // 如果文件已存在,也会返回 false,所以该值不能作为是否能创建的依据,因此不对其进行处理 + //noinspection ResultOfMethodCallIgnored + dir.mkdirs(); if (dir.exists()) { return true; } - ThreadUtil.sleep(1); + ThreadUtil.sleep(sleepMillis); } return dir.exists(); } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java index c247185b2..806105051 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java @@ -117,13 +117,13 @@ public class NioUtil { * @link http://androidxref.com/6.0.1_r10/xref/libcore/luni/src/main/java/java/nio/FileChannelImpl.java * @link http://androidxref.com/7.0.0_r1/xref/libcore/ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java * @link http://androidxref.com/7.0.0_r1/xref/libcore/ojluni/src/main/native/FileChannelImpl.c - * @since 2022-01-29 * @author z8g + * @since 5.7.21 */ private static long copySafely(FileChannel inChannel, FileChannel outChannel) throws IOException { - long totalBytes = inChannel.size(); + final long totalBytes = inChannel.size(); for (long pos = 0, remaining = totalBytes; remaining > 0; ) { // 确保文件内容不会缺失 - long writeBytes = inChannel.transferTo(pos, remaining, outChannel); // 实际传输的字节数 + final long writeBytes = inChannel.transferTo(pos, remaining, outChannel); // 实际传输的字节数 pos += writeBytes; remaining -= writeBytes; }