当前位置:  开发笔记 > 编程语言 > 正文

java有效地获取文件大小

如何解决《java有效地获取文件大小》经验,为你挑选了6个好方法。

谷歌搜索时,我发现使用java.io.File#length()可能很慢. FileChannel有一个size()方法也可用.

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



1> GHad..:

好吧,我试着用下面的代码测量它:

对于runs = 1和iterations = 1,URL方法最快,其次是channel.我跑了几次,暂停了大约10次.因此,对于一次性访问,使用URL是我能想到的最快方式:

LENGTH sum: 10626, per Iteration: 10626.0

CHANNEL sum: 5535, per Iteration: 5535.0

URL sum: 660, per Iteration: 660.0

对于runs = 5和iterations = 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 durations = new EnumMap(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 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;
    }

}


`stream.available()`不返回文件长度.它返回可用于读取的字节数,而不会阻塞其他流.它不一定与文件长度的字节数相同.要从流中获取实际长度,您确实需要**读取它(并同时计算读取的字节数).
这个基准是或者说它的解释是不正确的.在低迭代计数中,后面的测试利用了操作系统的文件缓存.在更高的迭代测试中,排名是正确的,但不是因为File.length()正在缓存某些东西,而仅仅因为其他2个选项基于相同的方法,但做了额外的工作,减慢了它们的速度.
除了InputStream.available()的javadoc之外,available()方法返回int的事实应该是针对URL方法的红色标志.尝试使用3GB文件,很明显它不是确定文件长度的有效方法.
@Paolo,缓存和优化文件系统访问是操作系统的主要职责之一.http://www.faqs.org/docs/linux_admin/buffer-cache.html要获得良好的基准测试结果,应在每次运行之前清除缓存.

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 times = new HashMap();
    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 entry : times.entrySet()) {
        System.out.println(
            entry.getKey() + " sum: " + 1e-3 * entry.getValue() +
            ", per Iteration: " + (1e-3 * entry.getValue() / runs / iterations));
    }
  }
}


实际上,虽然你说它衡量其他方面是正确的,但我的问题应该更清楚.我想要获取多个文件的文件大小,我希望以最快的方式.所以我确实需要考虑对象创建和开销,因为这是一个真实的场景
大约90%的时间花在了getResource上.我怀疑你需要使用反射来获取包含一些Java字节码的文件的名称.

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



4> tgdavies..:

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

长度总和:33,每次迭代:33.0

CHANNEL总和:3626,每次迭代:3626.0

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



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()是最快的方法.如果您打算将该文件用于读/写等其他目的,那么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 times = new HashMap();
        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 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";
        } 
    }
}



6> 小智..:

我遇到了同样的问题.我需要在网络共享上获取90,000个文件的文件大小和修改日期.使用Java,尽可能简约,需要很长时间.(我需要从文件中获取URL,以及对象的路径.所以它有所不同,但超过一个小时.)然后我使用本机Win32可执行文件,并执行相同的任务,只是转储文件路径,修改和控制台的大小,并从Java执行.速度惊人.本机进程和我读取数据的字符串处理可以每秒处理超过1000个项目.

因此,尽管人们对上述评论进行了排名,但这是一个有效的解决方案,并确实解决了我的问题.在我的情况下,我知道我需要提前大小的文件夹,我可以在命令行中将其传递给我的win32应用程序.我花了几个小时来处理一个目录到几分钟.

这个问题似乎也是Windows特有的.OS X没有相同的问题,可以像操作系统那样快速访问网络文件信息.

Windows上的Java文件处理非常糟糕.但是文件的本地磁盘访问很好.只是网络股票造成了可怕的表现.Windows可以获取网络共享信息,并在一分钟内计算总大小.

--Ben

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有