2012-07-16 6 views
13

mio caso d'uso mi richiede di aprire un file txt, dicono abc.txt che si trova all'interno di un archivio zip che contiene coppie chiave-valore nella formaModifica di un file di testo in un archivio ZIP in Java

key1 = valore1

chiave2 = valore2

.. e così via in cui ogni coppia di valori-chiave è in una nuova linea. Devo cambiare un valore corrispondente a un certo tasto e rimettere il file di testo in una nuova copia dell'archivio. Come faccio a farlo in java?

Il mio tentativo finora:

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

Quindi, a parte scovare il flusso di output, cosa non funziona? – MadProgrammer

+0

Non ti ho preso. Non ho svuotato esplicitamente il flusso di output. – Prabhakar

risposta

10

Lei aveva quasi capito bene. Una possibile ragione, il file è stato mostrato come corrotto è che si potrebbe avere utilizzato

zos.putNextEntry (entryIn)

nella parte altra pure. Questo crea una nuova voce nel file zip contenente informazioni dal file zip esistente. Le informazioni esistenti contengono il nome della voce (nome del file) e il suo CRC tra le altre cose.

E quindi, quando si tenta di aggiornare il file di testo e chiudere il file zip, genererà un errore come il CRC definito nella voce e il CRC dell'oggetto che si sta tentando di scrivere differiscono.

anche u potrebbe ottenere un errore se la lunghezza del testo che si sta cercando di sostituire è differente rispetto a quello esistente per esempio, si sta cercando di sostituire

chiave1 = valore1

con

chiave1 = val1

Si riduce al problema che il buffer a cui si sta tentando di scrivere ha una lunghezza diversa da quella specificata.

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(); 

Il codice seguente assicura che anche se i dati che viene sostituito è minore lunghezza rispetto alla lunghezza originale, non si verificano IndexOutOfBoundsExceptions.

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

+2

Complimenti, questa risposta dovrebbe essere accettata! – sunlock

+2

Si dovrebbe passare l'offset e la lunghezza all'inzio della stringa dall'array di byte, oppure si rischia che quando len sia inferiore al buffer, la stringa viene comunque creata utilizzando l'intero buffer. Questo potrebbe funzionare anche la maggior parte del tempo! Errore grave nel codice sopra! – Neil

+0

Se si modifica il file DOPO aver creato ZipEntry, quest'ultimo non sarà coerente con i dati (ad esempio CRC). Non è un problema? – GregT

0

solo un piccolo miglioramento 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); 
    } 
} 

Questo dovrebbe essere:

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

Per catturare tutte le occorrenze

La ragione è che, con il primo , potresti avere "chiave1" in una lettura e "= valore1" nella successiva, non essere in grado di catturare l'occorrenza che desideri modificare

2

Java 7 ha introdotto un modo molto più semplice per eseguire manipolazioni di archivi zip - API FileSystems, che consente di accedere al contenuto di un file come file system .

Oltre a API molto più semplici, sta eseguendo la modifica sul posto e non richiede la riscrittura di altri file (irrilevanti) in un archivio zip (come fatto nella risposta accettata).

Ecco il codice di esempio che risolve caso d'uso del PO:

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

Per ulteriori esempi di manipolazione archivio zip, vedere demo/nio/zipfs/Demo.java campione che può essere scaricato here (cercare JDK 8 Demo e campioni).

Problemi correlati