使用 Java 计算目录中的文件数

如何使用 Java 计算目录中的文件数?为了简单起见,让我们假设该目录没有任何子目录。

我知道标准的方法是:

new File(<directory path>).listFiles().length

但是这将有效地遍历目录中的所有文件,如果文件数量很大,这可能会花费很长时间。另外,我不关心目录中的实际文件,除非它们的数目大于某个固定的大数(比如5000)。

我猜是这样,但是目录(或者 Unix 中的 i 节点)不是存储包含在其中的文件数吗?如果我能直接从文件系统中获取这个数字,就会快得多。在后端开始进行实际处理之前,我需要检查 Tomcat 服务器上的每个 HTTP 请求。因此,速度至关重要。

我可以每隔一段时间运行一个守护进程来清除目录。我知道,所以请不要给我那个解决方案。

165482 次浏览

不幸的是,我认为这已经是最好的方法(尽管 list()listFiles()稍微好一点,因为它不构造 File对象)。

啊... ... 在 Java 中没有一个简单的方法来做这件事的基本原理是文件存储抽象: 一些文件系统可能没有一个目录中现成可用的文件数量... ... 这个计数可能根本没有任何意义(例如分布式的,P2P 文件系统,将文件列表存储为链表的 fs,或者数据库支持的文件系统... ...)。 所以是的,

new File(<directory path>).list().length

可能是你最好的选择。

不幸的是,正如 mmyers 所说,File.list ()的速度几乎和你使用 Java 的速度一样快。如果速度像您说的那样重要,那么您可能需要考虑使用 JNI执行这个特定的操作。然后,您可以根据特定的情况和文件系统定制代码。

这可能不适合您的应用程序,但是您总是可以尝试一个本机调用(使用 jni 或 Jna) ,或者执行一个特定于平台的命令并在返回 list ()之前读取输出。长度。在 * nix 上,您可以执行 ls -1a | wc -l(注意-第一个命令是横线 -1-a,第二个命令是横线 -1-L)。不知道什么是正确的窗口-也许只是一个 dir和寻找摘要。

在讨论这类问题之前,我强烈建议您创建一个包含大量文件的目录,并查看 list ()。长度确实太长了。正如 这个博主所建议的那样,您可能不想为此操心。

我可能会选瓦尔汗的答案。

如果你的目录中真的包含(> 100’000)很多文件,这里有一个(不可移植的)方法:

String directoryPath = "a path";


// -f flag is important, because this way ls does not sort it output,
// which is way faster
String[] params = { "/bin/sh", "-c",
"ls -f " + directoryPath + " | wc -l" };
Process process = Runtime.getRuntime().exec(params);
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
String fileCount = reader.readLine().trim() - 2; // accounting for .. and .
reader.close();
System.out.println(fileCount);

使用 sigar 应该会有帮助。 Sigar有本地钩子来获得统计数据

new Sigar().getDirStat(dir).getTotal()

由于您实际上并不需要总数,并且实际上希望在某个数字之后执行某个操作(在您的示例中为5000) ,因此可以使用 java.nio.file.Files.newDirectoryStream。这样做的好处是,您可以提前退出,而不必为了获得计数而遍历整个目录。

public boolean isOverMax(){
Path dir = Paths.get("C:/foo/bar");
int i = 1;


try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path p : stream) {
//larger than max files, exit
if (++i > MAX_FILES) {
return true;
}
}
} catch (IOException ex) {
ex.printStackTrace();
}


return false;
}

DirectoryStream接口文件也有一些很好的例子。

从 Java8开始,可以用三行代码完成:

try (Stream<Path> files = Files.list(Paths.get("your/path/here"))) {
long count = files.count();
}

关于5000个子节点和 inode 方面:

这个方法会遍历这些条目,但是正如 Varkhan 建议的那样,除了使用 JNI 或直接系统命令调用之外,您可能不能做得更好,但是即使这样,您也永远不能确定这些方法不会做同样的事情!

然而,让我们深入一点:

查看 JDK8源代码,Files.list公开了一个使用来自 Files.newDirectoryStreamIterable溪流,该 Iterable委托给 FileSystemProvider.newDirectoryStream

在 UNIX 系统上(反编译的 sun.nio.fs.UnixFileSystemProvider.class) ,它加载一个迭代器: 使用 sun.nio.fs.UnixSecureDirectoryStream(在遍历目录时使用文件锁)。

因此,有一个迭代器将循环遍历这里的条目。

现在,让我们看看计数机制。

实际的计数由 Java8流公开的计数/和减少 API 执行。理论上,这个 API 可以不费吹灰之力(通过多线程处理)执行并行操作。然而,流是创建与并行禁用,所以这是一个没有去..。

这种方法的 好的一面是,当底层(文件系统) API 读取作为条目的 它不会在内存中加载数组时,它将由迭代器计数。

最后,从概念上讲,在文件系统中,目录节点不需要保存它所包含的文件的 号码,它可以 只是包含它的子节点列表(inode 列表)。我不是文件系统方面的专家,但我相信 UNIX 文件系统就是这样工作的。因此,您不能假定有一种方法可以直接获得这些信息(即: 总是有一些子节点列表隐藏在某处)。

public void shouldGetTotalFilesCount() {
Integer reduce = of(listRoots()).parallel().map(this::getFilesCount).reduce(0, ((a, b) -> a + b));
}


private int getFilesCount(File directory) {
File[] files = directory.listFiles();
return Objects.isNull(files) ? 1 : Stream.of(files)
.parallel()
.reduce(0, (Integer acc, File p) -> acc + getFilesCount(p), (a, b) -> a + b);
}

在春季批次我做了以下

private int getFilesCount() throws IOException {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("file:" + projectFilesFolder + "/**/input/splitFolder/*.csv");
return resources.length;
}

这个方法对我很有效。

    // Recursive method to recover files and folders and to print the information
public static void listFiles(String directoryName) {


File file = new File(directoryName);
File[] fileList = file.listFiles(); // List files inside the main dir
int j;
String extension;
String fileName;


if (fileList != null) {
for (int i = 0; i < fileList.length; i++) {
extension = "";
if (fileList[i].isFile()) {
fileName = fileList[i].getName();


if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) {
extension = fileName.substring(fileName.lastIndexOf(".") + 1);
System.out.println("THE " + fileName + "  has the extension =   " + extension);
} else {
extension = "Unknown";
System.out.println("extension2 =    " + extension);
}


filesCount++;
allStats.add(new FilePropBean(filesCount, fileList[i].getName(), fileList[i].length(), extension,
fileList[i].getParent()));
} else if (fileList[i].isDirectory()) {
filesCount++;
extension = "";
allStats.add(new FilePropBean(filesCount, fileList[i].getName(), fileList[i].length(), extension,
fileList[i].getParent()));
listFiles(String.valueOf(fileList[i]));
}
}
}
}

计算目录和所有子目录中的文件。

var path = Path.of("your/path/here");
var count = Files.walk(path).filter(Files::isRegularFile).count();