diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java index ef0737880..298efc744 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java @@ -12,6 +12,7 @@ package org.dromara.hutool.core.io.file; +import org.dromara.hutool.core.collection.set.SetUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.util.CharsetUtil; @@ -23,6 +24,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import java.util.Set; /** * NIO中Path对象操作封装 @@ -71,11 +73,26 @@ public class PathUtil { * @since 5.4.1 */ public static List loopFiles(final Path path, final int maxDepth, final FileFilter fileFilter) { + return loopFiles(path, maxDepth, false, fileFilter); + } + + /** + * 递归遍历目录以及子目录中的所有文件
+ * 如果提供path为文件,直接返回过滤结果 + * + * @param path 当前遍历文件或目录 + * @param maxDepth 遍历最大深度,-1表示遍历到没有目录为止 + * @param isFollowLinks 是否跟踪软链(快捷方式) + * @param fileFilter 文件过滤规则对象,选择要保留的文件,只对文件有效,不过滤目录,null表示接收全部文件 + * @return 文件列表 + * @since 5.4.1 + */ + public static List loopFiles(final Path path, final int maxDepth, final boolean isFollowLinks, final FileFilter fileFilter) { final List fileList = new ArrayList<>(); - if (null == path || !Files.exists(path)) { + if (!exists(path, isFollowLinks)) { return fileList; - } else if (!isDirectory(path)) { + } else if (!isDirectory(path, isFollowLinks)) { final File file = path.toFile(); if (null == fileFilter || fileFilter.accept(file)) { fileList.add(file); @@ -83,7 +100,7 @@ public class PathUtil { return fileList; } - walkFiles(path, maxDepth, new SimpleFileVisitor() { + walkFiles(path, maxDepth, isFollowLinks, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs) { @@ -119,14 +136,28 @@ public class PathUtil { * @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor) * @since 4.6.3 */ - public static void walkFiles(final Path start, int maxDepth, final FileVisitor visitor) { + public static void walkFiles(final Path start, final int maxDepth, final FileVisitor visitor) { + walkFiles(start, maxDepth, false, visitor); + } + + /** + * 遍历指定path下的文件并做处理 + * + * @param start 起始路径,必须为目录 + * @param maxDepth 最大遍历深度,-1表示不限制深度 + * @param visitor {@link FileVisitor} 接口,用于自定义在访问文件时,访问目录前后等节点做的操作 + * @param isFollowLinks 是否追踪到软链对应的真实地址 + * @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor) + * @since 5.8.23 + */ + public static void walkFiles(final Path start, int maxDepth, final boolean isFollowLinks, final FileVisitor visitor) { if (maxDepth < 0) { // < 0 表示遍历到最底层 maxDepth = Integer.MAX_VALUE; } try { - Files.walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), maxDepth, visitor); + Files.walkFileTree(start, getFileVisitOption(isFollowLinks), maxDepth, visitor); } catch (final IOException e) { throw new IORuntimeException(e); } @@ -238,8 +269,7 @@ public class PathUtil { if (null == path) { return false; } - final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS}; - return Files.isDirectory(path, options); + return Files.isDirectory(path, getLinkOptions(isFollowLinks)); } /** @@ -323,9 +353,8 @@ public class PathUtil { return null; } - final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS}; try { - return Files.readAttributes(path, BasicFileAttributes.class, options); + return Files.readAttributes(path, BasicFileAttributes.class, getLinkOptions(isFollowLinks)); } catch (final IOException e) { throw new IORuntimeException(e); } @@ -497,8 +526,7 @@ public class PathUtil { if (null == path) { return false; } - final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS}; - return Files.isRegularFile(path, options); + return Files.isRegularFile(path, getLinkOptions(isFollowLinks)); } /** @@ -535,8 +563,7 @@ public class PathUtil { if (null == path) { return false; } - final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS}; - return Files.exists(path, options); + return Files.exists(path, getLinkOptions(isFollowLinks)); } /** @@ -572,7 +599,7 @@ public class PathUtil { * @return 实际路径 * @throws IORuntimeException IO异常,如文件不存在等 */ - public static Path toRealPath(Path path) throws IORuntimeException{ + public static Path toRealPath(Path path) throws IORuntimeException { if (null != path) { try { path = path.toRealPath(); @@ -663,11 +690,35 @@ public class PathUtil { } else { return Files.createTempFile(mkdir(dir), prefix, suffix); } - } catch (final IOException ioex) { // fixes java.io.WinNTFileSystem.createFileExclusively access denied + } catch (final IOException ioex) { + // fixes java.io.WinNTFileSystem.createFileExclusively access denied if (++exceptionsCount >= 50) { throw new IORuntimeException(ioex); } } } } + + /** + * 构建是否追踪软链的选项 + * + * @param isFollowLinks 是否追踪软链 + * @return 选项 + * @since 5.8.23 + */ + public static LinkOption[] getLinkOptions(final boolean isFollowLinks) { + return isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS}; + } + + /** + * 构建是否追踪软链的选项 + * + * @param isFollowLinks 是否追踪软链 + * @return 选项 + * @since 5.8.23 + */ + public static Set getFileVisitOption(final boolean isFollowLinks) { + return isFollowLinks ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) : + EnumSet.noneOf(FileVisitOption.class); + } }