2012-07-16 23 views
13

Mi caso de uso me obliga a abrir un archivo txt, abc.txt decir que está dentro de un archivo zip que contiene pares de valores clave en la formaModificación de un archivo de texto en un archivo ZIP en Java

key1 = valor1

key2 = valor2

.. y así sucesivamente, donde cada par clave-valor está en una nueva línea. Tengo que cambiar un valor correspondiente a una determinada clave y volver a colocar el archivo de texto en una nueva copia del archivo. ¿Cómo hago esto en Java?

Mi intento hasta el momento:

ZipFile zipFile = new ZipFile("test.zip"); 
    final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("out.zip")); 
    for(Enumeration e = zipFile.entries(); e.hasMoreElements();) { 
     ZipEntry entryIn = (ZipEntry) e.nextElement(); 
     if(!entryIn.getName().equalsIgnoreCase("abc.txt")){ 
      zos.putNextEntry(entryIn); 
      InputStream is = zipFile.getInputStream(entryIn); 
      byte [] buf = new byte[1024]; 
      int len; 
      while((len = (is.read(buf))) > 0) {    
       zos.write(buf, 0, len); 
      } 
     } 
     else{ 
      // I'm not sure what to do here 
      // Tried a few things and the file gets corrupt 
     } 
     zos.closeEntry(); 
    } 
    zos.close(); 
+0

Entonces, ¿otro que enjuagar el flujo de salida? ¿Qué no funciona? – MadProgrammer

+0

No te entendí. No he purgado explícitamente el flujo de salida. – Prabhakar

Respuesta

10

Casi se había hecho bien. Una posible razón, el archivo se muestra como corrupto es que podría haber utilizado

zos.putNextEntry (entryIn)

en la parte más también. Esto crea una nueva entrada en el archivo zip que contiene información del archivo zip existente. La información existente contiene el nombre de la entrada (nombre del archivo) y su CRC, entre otras cosas.

Y luego, cuando intente actualizar el archivo de texto y cerrar el archivo zip, arrojará un error ya que el CRC definido en la entrada y el CRC del objeto que está tratando de escribir son diferentes.

también u pudo obtener un error si la longitud del texto que usted está tratando de reemplazar es diferente a la existente es decir, que están tratando de reemplazar

clave1 = valor1

con

key1 = val1

Esto se reduce al problema de que el búfer en el que está intentando escribir tiene una longitud diferente a la especificada.

ZipFile zipFile = new ZipFile("test.zip"); 
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("out.zip")); 
for(Enumeration e = zipFile.entries(); e.hasMoreElements();) { 
    ZipEntry entryIn = (ZipEntry) e.nextElement(); 
    if (!entryIn.getName().equalsIgnoreCase("abc.txt")) { 
     zos.putNextEntry(entryIn); 
     InputStream is = zipFile.getInputStream(entryIn); 
     byte[] buf = new byte[1024]; 
     int len; 
     while((len = is.read(buf)) > 0) {    
      zos.write(buf, 0, len); 
     } 
    } 
    else{ 
     zos.putNextEntry(new ZipEntry("abc.txt")); 

     InputStream is = zipFile.getInputStream(entryIn); 
     byte[] buf = new byte[1024]; 
     int len; 
     while ((len = (is.read(buf))) > 0) { 
      String s = new String(buf); 
      if (s.contains("key1=value1")) { 
       buf = s.replaceAll("key1=value1", "key1=val2").getBytes(); 
      } 
      zos.write(buf, 0, (len < buf.length) ? len : buf.length); 
     } 
    } 
    zos.closeEntry(); 
} 
zos.close(); 

El siguiente código asegura que incluso si los datos que se reemplaza es de menor longitud que la longitud original, no se producen IndexOutOfBoundsExceptions.

(len < buf.length)? len: buf.longitud

+2

Felicitaciones, esta respuesta debe ser aceptada! – sunlock

+2

Debería pasar el desplazamiento y la longitud a la iniciación de cadena desde la matriz de bytes, o corre el riesgo de que cuando len sea menor que el búfer, la cadena aún se cree usando todo el búfer. ¡Esto incluso puede funcionar la mayor parte del tiempo! ¡Error grave en el código anterior! – Neil

+0

Si modifica el archivo DESPUÉS de crear ZipEntry, este último no será coherente con los datos (CRC, por ejemplo). ¿No es un problema? – GregT

0

Sólo una pequeña mejora a:

else{ 
    zos.putNextEntry(new ZipEntry("abc.txt")); 

    InputStream is = zipFile.getInputStream(entryIn); 
    byte[] buf = new byte[1024]; 
    int len; 
    while ((len = (is.read(buf))) > 0) { 
     String s = new String(buf); 
     if (s.contains("key1=value1")) { 
      buf = s.replaceAll("key1=value1", "key1=val2").getBytes(); 
     } 
     zos.write(buf, 0, (len < buf.length) ? len : buf.length); 
    } 
} 

Eso debería ser:

else{ 
    zos.putNextEntry(new ZipEntry("abc.txt")); 

    InputStream is = zipFile.getInputStream(entryIn); 
    long size = entry.getSize(); 
    if (size > Integer.MAX_VALUE) { 
     throw new IllegalStateException("..."); 
    } 
    byte[] bytes = new byte[(int)size]; 
    is.read(bytes); 
    zos.write(new String(bytes).replaceAll("key1=value1", "key1=val2").getBytes()); 
} 

Con el fin de capturar todas las ocurrencias

La razón es que, con la primera , podría tener "clave1" en una lectura y "= valor1" en la siguiente, no pudiendo capturar la ocurrencia que desea cambiar

2

Java 7 introdujo una forma mucho más simple para hacer manipulaciones de archivos zip - FileSystems API, que permite acceder a los contenidos de un archivo como un sistema de archivos .

Además de API mucho más directa, está realizando la modificación in situ y no requiere reescribir otros archivos (irrelevantes) en un archivo zip (como se hace en la respuesta aceptada).

Aquí es código de ejemplo que resuelve caso de uso de OP:

import java.io.*; 
import java.nio.file.*; 

public static void main(String[] args) throws IOException { 
    modifyTextFileInZip("test.zip"); 
} 

static void modifyTextFileInZip(String zipPath) throws IOException { 
    Path zipFilePath = Paths.get(zipPath); 
    try (FileSystem fs = FileSystems.newFileSystem(zipFilePath, null)) { 
     Path source = fs.getPath("/abc.txt"); 
     Path temp = fs.getPath("/___abc___.txt"); 
     if (Files.exists(temp)) { 
      throw new IOException("temp file exists, generate another name"); 
     } 
     Files.move(source, temp); 
     streamCopy(temp, source); 
     Files.delete(temp); 
    } 
} 

static void streamCopy(Path src, Path dst) throws IOException { 
    try (BufferedReader br = new BufferedReader(
      new InputStreamReader(Files.newInputStream(src))); 
     BufferedWriter bw = new BufferedWriter(
      new OutputStreamWriter(Files.newOutputStream(dst)))) { 

     String line; 
     while ((line = br.readLine()) != null) { 
      line = line.replace("key1=value1", "key1=value2"); 
      bw.write(line); 
      bw.newLine(); 
     } 
    } 
} 

Para más ejemplos de manipulación de archivo zip, ver demo/nio/zipfs/Demo.java muestra, que se puede descargar here (busque JDK 8 Demostraciones y Muestras).

Cuestiones relacionadas