簡體   English   中英

使用Java修改ZIP存檔中的文本文件

[英]Modifying a text file in a ZIP archive in Java

我的用例要求我打開一個txt文件,比如abc.txt,它位於一個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();

你幾乎做對了。 一個可能的原因,文件被顯示為已損壞,您可能已經使用過

zos.putNextEntry(entryIn)

在其他部分也是如此。 這將在zip文件中創建一個包含現有zip文件信息的新條目。 現有信息包含條目名稱(文件名)及其CRC等。

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

如果您要替換的文本長度與現有文本的長度不同(即您嘗試替換),也可能會出錯

鍵1 =值

鍵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.length

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示例,您可以在此處下載(查找JDK 8演示和示例)。

只有一點點改進:

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”,但是無法捕獲要更改的事件

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM