2012-07-16 355 views
13

我的用例需要我打開一個txt文件,說的abc.txt這是包含在形式修改ZIP檔案文本文件中的Java

鍵1鍵 - 值對一個zip壓縮包內=值1

鍵2 =值

..等,其中每個鍵 - 值對是在一個新行。 我必須更改與某個特定鍵相對應的一個值,並將該文本文件重新放回到存檔的新副本中。我如何在java中做到這一點?

我迄今爲止嘗試:

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

那麼,其他然後刷新輸出流,有什麼不工作? – MadProgrammer 2012-07-16 10:25:16

+0

我沒有得到你。我沒有明確地刷新輸出流。 – Prabhakar 2012-07-17 04:19:50

回答

10

你幾乎是正確的。一個可能的原因,該文件顯示爲損壞的是,你可能已經使用

zos.putNextEntry(entryIn)

在其他部分

爲好。這會在包含來自現有zip文件的信息的zip文件中創建一個新條目。現有信息包含條目名稱(文件名稱)及其CRC等。

然後,當您嘗試更新文本文件並關閉zip文件時,它將引發錯誤,因爲條目中定義的CRC和您嘗試寫入的對象的CRC不同。

此外,如果你正在試圖取代文本的長度是一個比現有的即你正在試圖取代

key1的不同ü可能得到錯誤=值

鍵1 = VAL1

這可以歸結爲您嘗試寫入的緩衝區長度與指定的緩衝區長度不同的問題。

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

以下代碼確保即使被替換的數據是比原來的長度的長度以下,不會發生IndexOutOfBoundsExceptions。

(len < buf.length)? len:buf。長度

+2

榮譽,這個答覆應該被接受! – sunlock 2014-12-19 10:10:33

+2

您應該將偏移量和長度傳遞給字節數組中的字符串初始化,否則您有可能會在len小於緩衝區時仍然使用整個緩衝區創建字符串。這甚至可能在大部分時間都有效!上述代碼嚴重錯誤! – Neil 2017-01-06 11:06:50

+0

如果在創建ZipEntry之後修改文件,則後者將與數據不一致(例如CRC)。這不是問題嗎? – GregT 2017-09-05 15:09:46

0

只有一點點改進:

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

這應該是:

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

爲了捕獲所有出現

的原因是,與第一,您可以在一次讀取中獲得「key1」,然後在下一次獲得「= value1」,無法捕獲您想要更改的事件

2

Java 7引入了一種更簡單的方法來執行zip歸檔操作 - FileSystems API,它允許以文件系統訪問文件內容。

除了更直接的API之外,它正在進行修改,並且不需要在zip存檔中重寫其他(不相關的)文件(如接受的答案中所做的那樣)。

下面是解決了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(); 
     } 
    } 
} 

更多zip壓縮包操作示例,請參閱demo/nio/zipfs/Demo.java樣品,你可以下載here(尋找JDK 8演示和樣本)。