2012-07-16 10 views
13

Mój przypadek użycia wymaga mnie do otwarcia pliku txt, powiedzmy abc.txt który znajduje się wewnątrz archiwum zip, który zawiera pary klucz-wartość w postaciModyfikowanie pliku tekstowego w formie archiwum ZIP w Javie

klucz1 = value1

klucz2 = value2

.. i tak dalej, gdzie każda para klucz-wartość jest w nowej linii. Muszę zmienić jedną wartość odpowiadającą określonemu kluczowi i umieścić plik tekstowy z powrotem w nowej kopii archiwum. Jak to zrobić w java?

Moja próba tak daleko:

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

A więc inne niż wypłukanie strumienia wyjściowego, co nie działa? – MadProgrammer

+0

Nie dostałem cię. Nie opróżniałem wyraźnie strumienia wyjściowego. – Prabhakar

Odpowiedz

10

Jesteś już prawie miał rację. Jedną z możliwych przyczyn, plik został pokazany jako uszkodzony jest to, że może użyłeś

zos.putNextEntry (entryIn)

w ramach innego, jak dobrze. Spowoduje to utworzenie nowego wpisu w pliku zip zawierającego informacje z istniejącego pliku zip. Istniejące informacje zawierają między innymi nazwę wpisu (nazwę pliku) i jego CRC.

A następnie, gdy spróbujesz zaktualizować plik tekstowy i zamknąć plik zip, spowoduje to błąd, ponieważ CRC zdefiniowane we wpisie i CRC obiektu, który próbujesz napisać, różnią się.

także u może się błąd jeśli długość tekstu, który próbuje się zastąpić jest inny niż ten istniejący czyli próbujesz zastąpić

klucz1 = wartość1

z

klucz1 = wart1

Sprowadza się to do problemu, w którym bufor, do którego próbujesz pisać, ma inną długość niż podany.

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

Poniższy kod gwarantuje, że nawet jeśli dane, które są wymieniane jest mniej niż długości pierwotnej długości, nie występują żadne IndexOutOfBoundsExceptions.

(len < buf.length)? len: buf.Długość

+2

Kudos, ta odpowiedź powinna zostać przyjęta! – sunlock

+2

Powinieneś przekazywać przesunięcie i długość do inicjacji String z tablicy bajtów, lub ryzykujesz, że gdy len jest mniejszy niż bufor, Ciąg nadal będzie tworzony przy użyciu całego bufora. To może nawet działać przez większość czasu! Poważny błąd w powyższym kodzie! – Neil

+0

Jeśli zmodyfikujesz plik PO utworzeniu ZipEntry, to drugie nie będzie zgodne z danymi (na przykład CRC). Czy to nie problem? – GregT

0

Tylko niewielka poprawa na:

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

To powinno być:

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

aby uchwycić wszystkie wystąpienia

Powodem jest to, że przy pierwszym , możesz mieć "key1" w jednym czytaniu i "= value1" w następnym, nie będąc w stanie uchwycić wystąpienia, które chcesz zmienić

2

Java 7 wprowadziła znacznie prostszy sposób manipulowania archiwami zip - FileSystems API, który umożliwia dostęp do zawartości pliku jako system plików.

Oprócz znacznie prostszego interfejsu API, wykonuje on modyfikację w miejscu i nie wymaga przepisywania innych (nieistotnych) plików w archiwum zip (tak jak w zaakceptowanej odpowiedzi).

Oto przykładowy kod, który rozwiązuje przypadek użycia 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(); 
     } 
    } 
} 

Więcej przykładów manipulacji archiwum zip, zobacz demo/nio/zipfs/Demo.java próbki, które można pobrać here (poszukaj JDK 8 Demos i próbek).

Powiązane problemy