java的有效获取文件大小(java get file size efficiently)

2019-06-17 13:27发布

虽然谷歌搜索,我看到使用java.io.File#length()可能会很慢。 FileChannel具有size()方法是可用的。

有没有在java中一种有效的方式来获取文件的大小?

Answer 1:

好吧,我试着用下面的代码来衡量它了:

对于运行= 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;
    }

}


Answer 2:

通过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));
    }
  }
}


Answer 3:

当他们访问同一个文件每个测试方法所有的测试用例在这个岗位是有缺陷的。 因此,磁盘缓存踢在测试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


Answer 4:

当我修改代码以通过使用绝对路径,而不是资源访问的文件,我得到不同的结果(1点运行,1次迭代和10万字节的文件 - 时间为10个字节的文件是相同的100,000字节)

长度之和:33,每迭代:33.0

CHANNEL总和:3626,每迭代:3626.0

URL总和:294,每次迭代:294.0



Answer 5:

为了响应rgrig的基准,所花费的时间来打开/关闭FileChannel&RandomAccessFile的情况下还需要考虑,因为这些类将打开一个流读取文件。

修改基准后,我得到了这些结果对一个85MB的文件1次迭代:

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()是这样做的最快方式。 如果你打算使用其他目的,如读/写文件,然后皇家空军似乎是一个更好的选择。 只是不要忘记关闭文件连接:-)

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";
        } 
    }
}


Answer 6:

我遇到了同样的问题。 我需要得到的文件大小和90000页的文件修改日期在网络共享上。 使用Java,并且是作为简约越好,这将需要很长的时间。 (我需要得到该文件的URL,以及对象的路径也是如此。因此,它有些变化,但一个多小时。)然后我用原生Win32可执行程序,并做了同样的任务,只是倾销文件路径,修改和大小的控制台,从Java执行的。 速度是惊人的。 本机的过程中,我的字符串处理读取的数据可以处理超过1000个项目第二。

因此,即使人们失望位列上述评论,这是一个有效的解决方案,并没有解决我的问题。 在我来说,我知道我需要的时间提前的大小的文件夹,我可以通过在命令行中我的Win32应用程序。 我从几个小时去处理一个目录来分钟。

这个问题也还似乎是Windows专用。 OS X并没有同样的问题,并可以快速访问网络文件信息的OS可以这样做。

在Windows上的Java文件处理是可怕的。 对于文件的本地磁盘访问是好的,但。 这只是网络惹的祸业绩股。 窗户可以在网络上得到共享信息和计算的总规模在一分钟之过。

--Ben



Answer 7:

如果你想在一个目录多个文件的文件大小,使用Files.walkFileTree 。 您可以从获得大小BasicFileAttributes ,你会收到。

这是更快然后调用.length()上的结果File.listFiles()或使用Files.size()上的结果Files.newDirectoryStream() 在我的测试情况下,它是大约要快100倍。



Answer 8:

其实,我认为,“LS”可能会更快。 肯定有一些Java中的问题处理得到文件信息。 不幸的是Windows递归最小二乘的不等价安全的方法。 (CMD.EXE的DIR / S能够感到困惑,并产生无限循环的错误)

在Windows XP,在局域网上访问服务器,它需要我中5秒的Windows来获得文件的计数的文件夹(33,000),总大小显示。

当我在Java的递归迭代通过这个,我花了5分钟。 我开始测量它需要做file.length(),file.lastModified(),和file.toURI()的时间,我发现的是,我的时间99%采取的是那些3个电话。 该3个电话我其实需要做的...

1000页的文件不同的是15ms的本地与服务器上1800ms。 Java中的服务器路径扫描是可笑缓慢。 如果本机OS可以在扫描快,同一文件夹中,为什么不能Java的?

作为一个更完整的测试,我用XP WineMerge比较修改日期和文件大小的服务器与本地文件上。 这是迭代的每个文件夹中的文件33000的整个目录树。 总时间7秒。 Java的:超过5分钟。

所以原来的语句和问题,从OP是真实的,有效的。 它不太明显与本地文件系统的时候。 做一个地方,有33,000项比较文件夹时,需要在的WinMerge 3秒,并在Java中需要32秒本地。 如此反复,JAVA与原生是10倍的增长放缓,这些基本的测试。

的Java 1.6.0_22(最新的),千兆网卡和网络连接,平是小于1ms(无论是在同一交换机)

Java是缓慢的。



Answer 9:

从GHAD的基准,有几个问题人提到:

1>像BalusC提到:stream.available()在这种情况下流动。

因为可用的()返回一个可以从该输入流不被此输入流的方法的下一次调用无阻塞读取(或跳过)的字节数的估计值

所以,第一次删除URL这种方法。

2>如图StuartH提到 - 的顺序进行试运行也使高速缓存的差异,因此采取了通过单独运行测试。


现在开始测试:

当信道一个单独运行:

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();
}


文章来源: java get file size efficiently
标签: java filesize