java obtener el tamaño del archivo de manera eficiente


Mientras googleo, veo que usando java.io.File#length() puede ser lento. FileChannel tiene un size() método que está disponible también.

¿Hay una manera eficiente en java para obtener el tamaño del archivo?

 156
Author: Unsigned, 2008-09-22

9 answers

Bueno, traté de medirlo con el siguiente código:

Para runs = 1 e iterations = 1 el método URL es el más rápido seguido por channel. Hago esto con una pausa fresca unas 10 veces. Así que para el acceso de una sola vez, el uso de la URL es la forma más rápida que se me ocurre:

LENGTH sum: 10626, per Iteration: 10626.0

CHANNEL sum: 5535, per Iteration: 5535.0

URL sum: 660, per Iteration: 660.0

Para corridas = 5 e iteraciones = 50 la imagen dibuja diferente.

LENGTH sum: 39496, per Iteration: 157.984

CHANNEL sum: 74261, per Iteration: 297.044

URL sum: 95534, per Iteration: 382.136

El archivo debe estar almacenando en caché las llamadas al sistema de archivos, mientras que los canales y la URL tienen algunos sobrecarga.

Código:

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

}
 99
Author: GHad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-02 03:12:23

El punto de referencia dado por GHad mide muchas otras cosas (como la reflexión, la creación de instancias de objetos, etc.).) además de conseguir la longitud. Si tratamos de deshacernos de estas cosas, entonces por una llamada obtengo los siguientes tiempos en microsegundos:

   file sum___19.0, per Iteration___19.0
    raf sum___16.0, per Iteration___16.0
channel sum__273.0, per Iteration__273.0

Para 100 corridas y 10000 iteraciones obtengo:

   file sum__1767629.0, per Iteration__1.7676290000000001
    raf sum___881284.0, per Iteration__0.8812840000000001
channel sum___414286.0, per Iteration__0.414286

Ejecuté el siguiente código modificado dando como argumento el nombre de un archivo de 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));
    }
  }
}
 31
Author: logic.town,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-15 13:43:08

Todos los casos de prueba en este post son defectuosos ya que acceden al mismo archivo para cada método probado. Por lo tanto, el almacenamiento en caché de disco se inicia en el que las pruebas 2 y 3 se benefician. Para probar mi punto, tomé el caso de prueba proporcionado por GHAD y cambié el orden de enumeración y debajo están los resultados.

Mirando el resultado creo que el archivo.length() es el ganador realmente.

El orden de la prueba es el orden de salida. Incluso se puede ver el tiempo tomado en mi máquina varió entre las ejecuciones, pero el archivo.Length () cuando no primero, e incurrir en el primer acceso al disco ganó.

---
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
 16
Author: StuartH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-22 01:02:59

Cuando modifico tu código para usar un archivo al que se accede por una ruta absoluta en lugar de un recurso, obtengo un resultado diferente (para 1 ejecución, 1 iteración y un archivo de 100.000 bytes times las veces para un archivo de 10 bytes son idénticas a 100.000 bytes)

Suma de longitud: 33, por Iteración: 33.0

Suma DE canales: 3626, por Iteración: 3626.0

Suma de URL: 294, por Iteración: 294.0

 9
Author: tgdavies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 03:53:13

En respuesta al punto de referencia de rgrig, el tiempo necesario para abrir/cerrar las instancias FileChannel & RandomAccessFile también debe tenerse en cuenta, ya que estas clases abrirán una secuencia para leer el archivo.

Después de modificar el benchmark, obtuve estos resultados para 1 iteraciones en un archivo de 85MB:

file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)

Para 10000 iteraciones en el mismo archivo:

file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)

Si todo lo que necesita es el tamaño del archivo, file.length() es la forma más rápida de hacerlo. Si planea usar el archivo para otros propósitos como leer / escribir, entonces RAF parece ser una mejor apuesta. No olvides cerrar la conexión del archivo: -)

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";
        } 
    }
}
 8
Author: Karthikeyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-26 13:18:49

Me encontré con este mismo problema. Necesitaba obtener el tamaño del archivo y la fecha de modificación de 90,000 archivos en un recurso compartido de red. Usando Java, y siendo tan minimalista como sea posible, tomaría mucho tiempo. (Necesitaba obtener la URL del archivo, y la ruta del objeto también. Así que su variado algo, pero más de una hora. Luego usé un ejecutable nativo de Win32, e hice la misma tarea, simplemente volcando la ruta del archivo, modificado y tamaño a la consola, y lo ejecuté desde Java. Velocidad fue increíble. El proceso nativo y mi manejo de cadenas para leer los datos podrían procesar más de 1000 elementos por segundo.

Así que a pesar de que la gente clasificó el comentario anterior, esta es una solución válida, y resolvió mi problema. En mi caso sabía las carpetas que necesitaba los tamaños de antes de tiempo, y podía pasar eso en la línea de comandos a mi aplicación win32. Pasé de horas para procesar un directorio a minutos.

El problema también parecía ser específico de Windows. OS X no tenía el mismo problema y podría acceder a la información del archivo de red tan rápido como el sistema operativo podría hacerlo.

El manejo de archivos Java en Windows es terrible. Sin embargo, el acceso al disco local para los archivos está bien. Solo fueron las acciones de la red las que causaron el terrible rendimiento. Windows podría obtener información sobre el recurso compartido de red y calcular el tamaño total en menos de un minuto también.

Ben Ben

 8
Author: Ben Spink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-02 03:25:53

Si desea el tamaño de archivo de varios archivos en un directorio, utilice Files.walkFileTree. Puedes obtener el tamaño del BasicFileAttributes que recibirás.

Esto es mucho más rápido que llamar a .length() en el resultado de File.listFiles() o usar Files.size() en el resultado de Files.newDirectoryStream(). En mis casos de prueba fue aproximadamente 100 veces más rápido.

 3
Author: Scg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-10 07:54:16

En realidad, creo que el "ls" puede ser más rápido. Definitivamente hay algunos problemas en Java que tratan de obtener información del archivo. Desafortunadamente no hay un método seguro equivalente de ls recursivo para Windows. (cmd.los DIR /S de exe pueden confundirse y generar errores en bucles infinitos)

En XP, accediendo a un servidor en la LAN, me toma 5 segundos en Windows para obtener el recuento de los archivos en una carpeta (33.000), y el tamaño total.

Cuando itero recursivamente a través de esto en Java, me lleva más de 5 minutos. Empecé a medir el tiempo que se tarda en hacer el archivo.length (), file.lastModified (), y file.toURI () y lo que encontré es que el 99% de mi tiempo es ocupado por esas 3 llamadas. Las 3 llamadas que realmente necesito hacer...

La diferencia para 1000 archivos es de 15 ms locales frente a 1800 ms en el servidor. El escaneo de la ruta del servidor en Java es ridículamente lento. Si el sistema operativo nativo puede ser rápido en el escaneo de esa misma carpeta, ¿por qué no puede Java?

Como una prueba más completa, usé WineMerge en XP para comparar la fecha de modificación y el tamaño de los archivos en el servidor con los archivos localmente. Esto era iterar sobre todo el árbol de directorios de 33.000 archivos en cada carpeta. Tiempo total, 7 segundos. java: más de 5 minutos.

Así que la declaración original y la pregunta del OP es verdadera y válida. Menos notable cuando se trata de un sistema de archivos local. Hacer una comparación local de la carpeta con 33,000 elementos toma 3 segundos en WinMerge, y toma 32 segundos localmente en Java. Así que de nuevo, java versus native es una desaceleración 10x en estas pruebas rudimentarias.

Java 1.6.0_22 (último), LAN Gigabit, y conexiones de red, ping es menos de 1ms (ambos en el mismo switch)

Java es lento.

 2
Author: Ben Spink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-17 07:40:25

Del punto de referencia de GHad, hay algunos temas que la gente ha mencionado:

1 > Como BalusC mencionó: stream.disponible() se fluye en este caso.

Because available() devuelve una estimación del número de bytes que se pueden leer (o saltar) de este flujo de entrada sin bloquearlo con la siguiente invocación de un método para este flujo de entrada.

So 1st to remove the URL this approach.

2 > Como StuartH mencionó - el orden en que se ejecuta la prueba también hace que diferencia de caché, así que sácalo ejecutando la prueba por separado.


Ahora comienza la prueba:

Cuando el CANAL uno se ejecuta solo:

CHANNEL sum: 59691, per Iteration: 238.764

Cuando la longitud uno corre solo:

LENGTH sum: 48268, per Iteration: 193.072

Así que parece que la longitud uno es el ganador aquí:

@Override
public long getResult() throws Exception {
    File me = new File(FileSizeBench.class.getResource(
            "FileSizeBench.class").getFile());
    return me.length();
}
 2
Author: Gob00st,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-17 14:54:36