2010-07-23 10 views
8

He notado que la facilidad de descompresión en Java es extremadamente lenta en comparación con el uso de una herramienta nativa como WinZip.Desempeño deficiente de las herramientas de descompresión de Java

¿Existe una biblioteca de terceros disponibles para Java que es más eficiente? Open Source es preferido.

Editar

Aquí es una comparación de velocidad usando la solución integrada en Java vs 7zip. Agregué flujos de entrada/salida almacenados en mi solución original (gracias a Jim, esto hizo una gran diferencia).

Zip Tamaño de archivo: 800K Solución de Java: 2,7 segundos solución 7Zip: 204 ms

Este es el código modificado utilizando el incorporado en Java descompresión:

/** Unpacks the give zip file using the built in Java facilities for unzip. */ 
@SuppressWarnings("unchecked") 
public final static void unpack(File zipFile, File rootDir) throws IOException 
{ 
    ZipFile zip = new ZipFile(zipFile); 
    Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries(); 
    while(entries.hasMoreElements()) { 
    ZipEntry entry = entries.nextElement(); 
    java.io.File f = new java.io.File(rootDir, entry.getName()); 
    if (entry.isDirectory()) { // if its a directory, create it 
     continue; 
    } 

    if (!f.exists()) { 
     f.getParentFile().mkdirs(); 
     f.createNewFile(); 
    } 

    BufferedInputStream bis = new BufferedInputStream(zip.getInputStream(entry)); // get the input stream 
    BufferedOutputStream bos = new BufferedOutputStream(new java.io.FileOutputStream(f)); 
    while (bis.available() > 0) { // write contents of 'is' to 'fos' 
     bos.write(bis.read()); 
    } 
    bos.close(); 
    bis.close(); 
    } 
} 
+1

he tenido ningún problema con las funciones descomprimir, y se han procesado 250 MB de archivos zip que contienen gzipped archivos de texto. ¿Qué estás haciendo que lleva tanto tiempo? ¿Es algo complejo? –

+0

¿Tal vez si lo haces con un hilo de baja prioridad? –

+0

Recomiendo encarecidamente: 'if (entry.getName(). Contains (" ..")) continue;' –

Respuesta

3

Asegúrese de que usted está alimentando el método de descompresión es BufferedInputStream en su aplicación Java. Si ha cometido el error de utilizar una secuencia de entrada sin búfer, su rendimiento de IO está garantizado para chupar.

-1

He encontrado una solución "poco elegante". Existe una utilidad de código abierto 7zip (www.7-zip.org) que es de uso gratuito. Puede descargar la versión de línea de comando (http://www.7-zip.org/download.html). 7-zip solo es compatible con Windows, pero parece que esto se ha transferido a otras plataformas (p7zip).

Obviamente, esta solución no es ideal, ya que es específica de la plataforma y se basa en un archivo ejecutable. Sin embargo, la velocidad en comparación con hacer el descomprimir en Java es increíble.

Aquí es el código de la función de utilidad que he creado para interactuar con esta utilidad. Hay margen de mejora, ya que el siguiente código es específico de Windows.

/** Unpacks the zipfile to the output directory. Note: this code relies on 7-zip 
    (specifically the cmd line version, 7za.exe). The exeDir specifies the location of the 7za.exe utility. */ 
public static void unpack(File zipFile, File outputDir, File exeDir) throws IOException, InterruptedException 
{ 
    if (!zipFile.exists()) throw new FileNotFoundException(zipFile.getAbsolutePath()); 
    if (!exeDir.exists()) throw new FileNotFoundException(exeDir.getAbsolutePath()); 
    if (!outputDir.exists()) outputDir.mkdirs(); 

    String cmd = exeDir.getAbsolutePath() + "/7za.exe -y e " + zipFile.getAbsolutePath(); 

    ProcessBuilder builder = new ProcessBuilder(new String[] { "cmd.exe", "/C", cmd }); 
    builder.directory(outputDir); 
    Process p = builder.start(); 
    int rc = p.waitFor(); 
    if (rc != 0) { 
    log.severe("Util::unpack() 7za process did not complete normally. rc: " + rc); 
    } 
}  
+1

-1 para una cop-out poderosamente barata. –

20

El problema no es la descompresión, que es la forma ineficiente que escribe los datos descomprimidos de vuelta al disco. Mis puntos de referencia muestran que el uso

InputStream is = zip.getInputStream(entry); // get the input stream 
    OutputStream os = new java.io.FileOutputStream(f); 
    byte[] buf = new byte[4096]; 
    int r; 
    while ((r = is.read(buf)) != -1) { 
     os.write(buf, 0, r); 
    } 
    os.close(); 
    is.close(); 

vez reduce el tiempo de ejecución del método en un factor de 5 (de 5 a 1 segundo para un archivo zip 6 MB).

La causa más probable es que su uso de bis.available(). Además de ser incorrecto (disponible devuelve el número de bytes hasta que se bloquee una llamada para leer, no hasta el final de la secuencia), esto omite el almacenamiento en búfer proporcionado por BufferedInputStream, requiriendo una llamada al sistema nativo para cada byte copiado en el archivo de salida.

Tenga en cuenta que el ajuste en un BufferedStream no es necesario si utiliza los métodos masivos de lectura y escritura como hago anteriormente, y que el código para cerrar los recursos no es una excepción segura (si la lectura o la escritura falla por alguna razón, is o os se cerrarán). Finalmente, si tiene IOUtils en la ruta de la clase, le recomiendo usar su bien probado IOUtils.copy en lugar de rodar el suyo.

+1

Gracias Meriton! Probé esto y el rendimiento ahora es comparable a 7zip. He añadido IOUtils a mi caja de herramientas para el futuro. Esta es una sugerencia muy buena. – Tony

+0

@tony ¿por qué no aceptaste la respuesta de Meritons? – t0r0X

Cuestiones relacionadas