2010-08-26 34 views
12

Tengo un requisito en el que tengo que crear un archivo zip a partir de una lista de archivos disponibles. Los archivos son de diferentes tipos, como txt, pdf, xml, etc. Estoy usando las clases de java util para hacerlo.Cómo estimar el tamaño del archivo zip en java antes de crearlo

El requisito aquí es mantener un tamaño de archivo máximo de 5 mb. Debo seleccionar los archivos de la lista según la marca de tiempo, agregar los archivos a zip hasta que el tamaño del archivo zip alcance 5 mb. Debería omitir los archivos restantes.

Por favor, avíseme si hay una forma en Java donde puedo estimar el tamaño del archivo comprimido por adelantado sin crear un archivo real?

O ¿hay algún otro enfoque para manejar esta

Respuesta

0

Creo que no hay alguna manera de estimar el tamaño de zip que se creó porque las cremalleras son procesados ​​como corrientes. Tampoco sería técnicamente posible predecir el tamaño del formato comprimido creado a menos que realmente lo comprimiera.

8

Envuelva su ZipOutputStream en un OutputStream personalizado, llamado aquí YourOutputStream.

  • El constructor de YourOutputStream creará otra ZipOutputStream (zos2) que envuelve un nuevo ByteArrayOutputStream (baos)
    public YourOutputStream(ZipOutputStream zos, int maxSizeInBytes)
  • Cuando se desea escribir un archivo con YourOutputStream, primero escriba en zos2
    public void writeFile(File file) throws ZipFileFullException
    public void writeFile(String path) throws ZipFileFullException
    etc ...
  • si baos.size() está bajo maxSizeInBytes
    • escribir el archivo en zos1
  • demás
    • cerca zos1, baos, un zos2 una excepción. Para la excepción, no puedo pensar en una ya existente, si la hay, úsala, sino crea tu propia IOException ZipFileFullException.

Se necesitan dos ZipOutputStream, uno para ser escrito en su unidad, uno para comprobar si su contenido es de más de 5 MB.

EDITAR: De hecho lo comprobé, you can't remove a ZipEntry easily.

http://download.oracle.com/javase/6/docs/api/java/io/ByteArrayOutputStream.html#size()

+0

Gracias por su ayuda. Como solo necesito un tamaño aproximado y puedo averiguar la relación de compresión para la mayoría de los tipos de archivos que utilizamos, utilicé el sugerido por Nate. Gracias de nuevo – Vignesh

0

Lo hice una vez en un proyecto con los tipos de entrada conocidos. Sabíamos que, hablando en general, nuestros datos comprimidos eran 5: 1 (era todo texto). Por lo tanto, verificaría el tamaño del archivo y lo dividiría entre 5 ...

En este caso, el propósito de hacerlo era compruebe que los archivos probablemente sean inferiores a un determinado tamaño. Solo necesitábamos una estimación aproximada.

Dicho todo esto, he notado que las aplicaciones zip como 7zip crearán un archivo zip de cierto tamaño (como un CD) y luego dividirán el zip en un nuevo archivo una vez que alcance el límite. Podrías mirar ese código fuente.De hecho, he usado la versión de línea de comando de esa aplicación en el código anterior. También tienen una biblioteca que puedes usar. No estoy seguro de lo bien que se integrará con Java.

Por lo que vale, también he usado una biblioteca llamada SharpZipLib. Fue muy bueno. Me pregunto si hay un puerto Java para ello.

1

+1 para Colin Herbert: agregue los archivos uno por uno, realice una copia de seguridad del paso anterior o elimine el último archivo si el archivo es demasiado grande. Solo quiero agregar algunos detalles:

La predicción es demasiado poco confiable. P.E. un PDF puede contener texto sin comprimir, y comprimir hasta el 30% del original, o contiene texto e imágenes ya comprimidos, comprimiéndose al 80%. Debería inspeccionar todo el PDF para comprobar su compresibilidad, básicamente, tener que comprimirlos.

Puede intentar una predicción estadística, pero eso podría reducir el número de intentos fallidos, pero aún tendría que implementar la recomendación anterior. Vaya primero con la implementación más simple y vea si es suficiente.

O bien, comprima los archivos individualmente, luego elija los archivos que no excederán los 5 MB si están unidos. Si el desempaquetado también está automatizado, puede vincular los archivos zip en un solo archivo zip descomprimido.

+0

Si esto no funciona, podría tener un archivo de más de 5 MB que contenga solo "aaaa ...", que se comprimiría lo suficiente como para caber en el código postal. –

+0

d'oh. ¿Puedo reclamar la estupidez de madrugada? – peterchen

+0

(fijo, por supuesto) – peterchen

1

Quizás podría agregar un archivo cada vez, hasta que alcance el límite de 5MB, y luego descartar el último archivo. Como @Gopi, no creo que haya ninguna manera de estimarlo sin comprimir el archivo.

Por supuesto, el tamaño del archivo no aumentará (¿o tal vez un poco, debido al encabezado zip?), Por lo que al menos tiene una estimación del "peor caso".

+0

Consulte "Factor de expansión máximo" en http://zlib.net/zlib_tech.html – snemarch

0

sólo quería compartir cómo se implementó de forma manual

  int maxSizeForAllFiles = 70000; // Read from property 
     int sizePerFile = 22000; // Red from property 
     /** 
     * Iterate all attachment list to verify if ZIP is required 
     */ 
     for (String attachFile : inputAttachmentList) { 
      File file = new File(attachFile); 
      totalFileSize += file.length(); 
      /** 
      * if ZIP required ??? based on the size 
      */ 
      if (file.length() >= sizePerFile) { 
       toBeZipped = true; 
       logger.info("File: " 
          + attachFile 
           + " Size: " 
           + file.length() 
           + " File required to be zipped, MAX allowed per file: " 
           + sizePerFile); 
       break; 
      } 
     } 
     /** 
     * Check if all attachments put together cross MAX_SIZE_FOR_ALL_FILES 
     */ 
     if (totalFileSize >= maxSizeForAllFiles) { 
      toBeZipped = true; 
     } 
     if (toBeZipped) { 
      // Zip Here iterating all attachments 
     } 
0

Hay una mejor opción. Crear un maniquí LengthOutputStream que simplemente cuenta los bytes escritos:

public class LengthOutputStream extends OutputStream { 

    private long length = 0L; 

    @Override 
    public void write(int b) throws IOException { 
     length++; 
    } 

    public long getLength() { 
     return length; 
    } 
} 

Usted puede simplemente conectar el LengthOutputStream a un ZipOutputStream:

public static long sizeOfZippedDirectory(File dir) throws FileNotFoundException, IOException { 
     try (LengthOutputStream sos = new LengthOutputStream(); 
      ZipOutputStream zos = new ZipOutputStream(sos);) { 
      ... // Add ZIP entries to the stream 
      return sos.getLength(); 
     } 
    } 

El objeto LengthOutputStream cuenta los bytes de la corriente, pero las tiendas de cremallera nada, entonces no hay límite de tamaño de archivo. Este método proporciona una estimación de tamaño precisa, pero casi tan lento como crear un archivo ZIP.

Cuestiones relacionadas