虽然使用谷歌搜索,但我发现使用java.io.File#length()
可能会很慢。 FileChannel
有一个size()
方法,也可用。java有效获取文件大小
在java中有没有一种有效的方法来获取文件大小?
虽然使用谷歌搜索,但我发现使用java.io.File#length()
可能会很慢。 FileChannel
有一个size()
方法,也可用。java有效获取文件大小
在java中有没有一种有效的方法来获取文件大小?
好,我试图用下面的代码来测量它:
对于运行= 1和迭代= 1层的URL的方法是最快最次,随后信道。我运行这个有一些暂停新鲜约10倍。因此,对于一次访问,使用URL是我能想到的最快的方法:
LENGTH sum: 10626, per Iteration: 10626.0
CHANNEL sum: 5535, per Iteration: 5535.0
URL sum: 660, per Iteration: 660.0
对于运行= 5和迭代= 50的图片吸引不同。
LENGTH sum: 39496, per Iteration: 157.984
CHANNEL sum: 74261, per Iteration: 297.044
URL sum: 95534, per Iteration: 382.136
文件必须缓存到文件系统的调用,而通道和URL有一些开销。
代码:
import java.io.*;
import java.net.*;
import java.util.*;
public enum FileSizeBench {
LENGTH {
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}
},
CHANNEL {
@Override
public long getResult() throws Exception {
FileInputStream fis = null;
try {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
fis = new FileInputStream(me);
return fis.getChannel().size();
} finally {
fis.close();
}
}
},
URL {
@Override
public long getResult() throws Exception {
InputStream stream = null;
try {
URL url = FileSizeBench.class
.getResource("FileSizeBench.class");
stream = url.openStream();
return stream.available();
} finally {
stream.close();
}
}
};
public abstract long getResult() throws Exception;
public static void main(String[] args) throws Exception {
int runs = 5;
int iterations = 50;
EnumMap<FileSizeBench, Long> durations = new EnumMap<FileSizeBench, Long>(FileSizeBench.class);
for (int i = 0; i < runs; i++) {
for (FileSizeBench test : values()) {
if (!durations.containsKey(test)) {
durations.put(test, 0l);
}
long duration = testNow(test, iterations);
durations.put(test, durations.get(test) + duration);
// System.out.println(test + " took: " + duration + ", per iteration: " + ((double)duration/(double)iterations));
}
}
for (Map.Entry<FileSizeBench, Long> entry : durations.entrySet()) {
System.out.println();
System.out.println(entry.getKey() + " sum: " + entry.getValue() + ", per Iteration: " + ((double)entry.getValue()/(double)(runs * iterations)));
}
}
private static long testNow(FileSizeBench test, int iterations)
throws Exception {
long result = -1;
long before = System.nanoTime();
for (int i = 0; i < iterations; i++) {
if (result == -1) {
result = test.getResult();
//System.out.println(result);
} else if ((result = test.getResult()) != result) {
throw new Exception("variance detected!");
}
}
return (System.nanoTime() - before)/1000;
}
}
当我修改代码以使用由绝对路径,而不是资源访问的文件,我得到不同的结果(1点运行,1次迭代和10万字节的文件 - 次为一10字节的文件是相同的100,000字节)
LENGTH总和:33,每次迭代:33.0
CHANNEL总和:3626,每次迭代:3626.0
URL总数:294,迭代次数:294.0
除了获取长度以外,GHad给出的基准测量还有很多其他的东西(例如反射,实例化对象等)。如果我们试图摆脱这些东西然后一个电话,我得到以下时间以微秒:
file sum___19.0, per Iteration___19.0 raf sum___16.0, per Iteration___16.0 channel sum__273.0, per Iteration__273.0
为100次和10000次迭代,我得到:
file sum__1767629.0, per Iteration__1.7676290000000001 raf sum___881284.0, per Iteration__0.8812840000000001 channel sum___414286.0, per Iteration__0.414286
我没有运行下面的修改代码给出一个100MB文件的名称作为参数。
import java.io.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
public class FileSizeBench {
private static File file;
private static FileChannel channel;
private static RandomAccessFile raf;
public static void main(String[] args) throws Exception {
int runs = 1;
int iterations = 1;
file = new File(args[0]);
channel = new FileInputStream(args[0]).getChannel();
raf = new RandomAccessFile(args[0], "r");
HashMap<String, Double> times = new HashMap<String, Double>();
times.put("file", 0.0);
times.put("channel", 0.0);
times.put("raf", 0.0);
long start;
for (int i = 0; i < runs; ++i) {
long l = file.length();
start = System.nanoTime();
for (int j = 0; j < iterations; ++j)
if (l != file.length()) throw new Exception();
times.put("file", times.get("file") + System.nanoTime() - start);
start = System.nanoTime();
for (int j = 0; j < iterations; ++j)
if (l != channel.size()) throw new Exception();
times.put("channel", times.get("channel") + System.nanoTime() - start);
start = System.nanoTime();
for (int j = 0; j < iterations; ++j)
if (l != raf.length()) throw new Exception();
times.put("raf", times.get("raf") + System.nanoTime() - start);
}
for (Map.Entry<String, Double> entry : times.entrySet()) {
System.out.println(
entry.getKey() + " sum: " + 1e-3 * entry.getValue() +
", per Iteration: " + (1e-3 * entry.getValue()/runs/iterations));
}
}
}
针对rgrig的基准,开/所花费的时间关闭FileChannel & RandomAccessFile的情况下还需要考虑,因为这些类将打开一个流读取文件。
修改基准后,我得到了这些结果为1次迭代上85MB的文件:
file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)
有关同一个文件10000次迭代:
file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)
如果你需要的是文件大小,file.length()是最快的方法。如果您打算将文件用于读/写等其他目的,那么RAF似乎是一个更好的选择。只是不要忘了关闭文件连接:-)
import java.io.File;
import java.io.FileInputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
public class FileSizeBench
{
public static void main(String[] args) throws Exception
{
int iterations = 1;
String fileEntry = args[0];
Map<String, Long> times = new HashMap<String, Long>();
times.put("file", 0L);
times.put("channel", 0L);
times.put("raf", 0L);
long fileSize;
long start;
long end;
File f1;
FileChannel channel;
RandomAccessFile raf;
for (int i = 0; i < iterations; i++)
{
// file.length()
start = System.nanoTime();
f1 = new File(fileEntry);
fileSize = f1.length();
end = System.nanoTime();
times.put("file", times.get("file") + end - start);
// channel.size()
start = System.nanoTime();
channel = new FileInputStream(fileEntry).getChannel();
fileSize = channel.size();
channel.close();
end = System.nanoTime();
times.put("channel", times.get("channel") + end - start);
// raf.length()
start = System.nanoTime();
raf = new RandomAccessFile(fileEntry, "r");
fileSize = raf.length();
raf.close();
end = System.nanoTime();
times.put("raf", times.get("raf") + end - start);
}
for (Map.Entry<String, Long> entry : times.entrySet()) {
System.out.println(entry.getKey() + " totalTime: " + entry.getValue() + " (" + getTime(entry.getValue()) + ")");
}
}
public static String getTime(Long timeTaken)
{
if (timeTaken < 1000) {
return timeTaken + " ns";
} else if (timeTaken < (1000*1000)) {
return timeTaken/1000 + " us";
} else {
return timeTaken/(1000*1000) + " ms";
}
}
}
其实,我认为“ls”可能会更快。 Java在处理获取文件信息时肯定存在一些问题。不幸的是,没有用于Windows的递归ls的等价安全方法。 (cmd.exe的DIR/S可能会感到困惑,并在无限循环中产生错误)
在XP上,访问局域网上的服务器,需要5秒钟才能得到文件夹中的文件数(33,000 )和总大小。
当我在Java中通过这个递归迭代时,它花费了我5分钟以上。我开始测量file.length(),file.lastModified()和file.toURI()所花的时间,我发现我的时间有99%是由这3次调用拍摄的。我实际上需要做的3个调用...
1000个文件的区别是15ms本地和1800ms服务器。 Java中的服务器路径扫描速度非常慢。如果本机操作系统可以快速扫描相同的文件夹,为什么不能Java?
作为一个更完整的测试,我使用XP上的WineMerge来比较服务器上的文件与本地文件的修改日期和大小。这是遍历每个文件夹中33,000个文件的整个目录树。总时间7秒。 java:超过5分钟。
所以来自OP的原始声明和问题是真实的和有效的。在处理本地文件系统时不太明显。在WinMerge中进行33,000个文件夹的本地比较需要3秒钟,而在Java中需要32秒。所以再次,Java与原生是这些基本测试的10倍放缓。
爪哇1.6.0_22(最新),千兆LAN,和网络连接,平是小于1ms(均在相同的开关)
Java是缓慢的。
这篇文章中的所有测试用例都存在缺陷,因为他们访问每个测试方法的相同文件。因此,测试2和3受益于磁盘缓存。为了证明我的观点,我采用了GHAD提供的测试用例,并改变了枚举的顺序,下面是结果。
看着结果我认为File.length()真的是赢家。
测试顺序是输出顺序。你甚至可以看到我的机器在执行过程中花费的时间不同,但File.Length()不是首先执行,并且首次获得磁盘访问权。
---
LENGTH sum: 1163351, per Iteration: 4653.404
CHANNEL sum: 1094598, per Iteration: 4378.392
URL sum: 739691, per Iteration: 2958.764
---
CHANNEL sum: 845804, per Iteration: 3383.216
URL sum: 531334, per Iteration: 2125.336
LENGTH sum: 318413, per Iteration: 1273.652
---
URL sum: 137368, per Iteration: 549.472
LENGTH sum: 18677, per Iteration: 74.708
CHANNEL sum: 142125, per Iteration: 568.5
我遇到了同样的问题。我需要在网络共享上获得90,000个文件的文件大小和修改日期。使用Java,尽可能简洁,需要很长时间。 (我需要从文件中获取URL以及对象的路径,所以它有所不同,但超过了一个小时)。然后,我使用了本机Win32可执行文件,并执行了相同的任务,只是转储文件路径,修改和大小,并从Java执行。速度非常惊人。本地进程和我的字符串处理来读取数据可以每秒处理超过1000个项目。
所以即使人们对上述评论进行排名,这是一个有效的解决方案,并且解决了我的问题。在我的情况下,我知道需要提前大小的文件夹,并且我可以将它通过命令行传递给我的win32应用程序。我从几个小时开始处理一个目录到几分钟。
该问题似乎也是Windows特定的。OS X没有相同的问题,并且可以像操作系统那样快速地访问网络文件信息。
Java在Windows上进行文件处理非常糟糕。本地磁盘访问文件虽然很好。这只是网络共享造成了糟糕的表现。 Windows可以获取网络共享信息并在一分钟内计算总大小。
--Ben
从GHAD的基准,有几个问题人提到:
1>像BalusC提到:stream.available()在这种情况下流动。
由于available()返回的估计值可以从此输入流中读取(或跳过)的字节数,而不会因下一次调用此输入流的方法而被阻塞。
所以首先删除这个URL的方法。
2>正如StuartH所述 - 测试运行的顺序也会导致高速缓存不同,因此请单独运行测试以取得测试结果。
现在开始测试:
当CHANNEL一个单独运行:
CHANNEL sum: 59691, per Iteration: 238.764
当单独一个长度运行:
LENGTH sum: 48268, per Iteration: 193.072
所以看起来像长度的一个是赢家这里:
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}
如果您想要目录中多个文件的文件大小,请使用Files.walkFileTree
。您可以从BasicFileAttributes
获取您将收到的尺寸。
这对于File.listFiles()
的结果调用.length()
或对Files.newDirectoryStream()
的结果使用Files.size()
要快得多。在我的测试案例中,它大约快了100倍。
你能否提供链接说File.length()“可以很慢”? – 2008-09-22 19:02:37
对不起,这里是链接 http://www.javaperformancetuning.com/tips/rawtips.shtml 搜索 “文件信息,如File.length()需要系统调用,可能会很慢。” 这真是一个令人困惑的陈述,似乎几乎认为这将是一个系统调用。 – joshjdevl 2008-09-22 19:53:25
无论您如何操作,获取文件长度都需要系统调用。如果它通过网络或其他非常慢的文件系统,速度可能会很慢。没有比File.length()更快的方法来获取它,而这里“慢”的定义只是意味着不要不必要地调用它。 – jsight 2008-09-22 20:18:12