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

我的用例要求我打开一个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

Java 7引入了一种更简单的方式来进行zip存档操作 – FileSystems API,它允许以文件系统的forms访问文件内容。

除了更直接的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”,但是无法捕获想要更改的事件