使用 Java 修改 ZIP 存档中的文本文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11502260/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Modifying a text file in a ZIP archive in Java
提问by Prabhakar
My use case requires me to open a txt file, say abc.txt which is inside a zip archive which contains key-value pairs in the form
我的用例要求我打开一个 txt 文件,比如说 abc.txt,它位于一个 zip 存档中,其中包含表单中的键值对
key1=value1
key2=value2
键 1=值 1
键2=值2
.. and so on where each key-value pair is in a new line. I have to change one value corresponding to a certain key and put the text file back in a new copy of the archive. How do I do this in java?
.. 等等,其中每个键值对都在一个新行中。我必须更改与某个键对应的一个值,然后将文本文件放回存档的新副本中。我如何在java中做到这一点?
My attempt so far:
到目前为止我的尝试:
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();
采纳答案by Shiva
You had almost got it right. One possible reason, the file was shown as corrupted is that you might have used
你几乎做对了。文件显示为损坏的一种可能原因是您可能使用过
zos.putNextEntry(entryIn)
zos.putNextEntry(entryIn)
in the else part as well. This creates a new entry in the zip file containing information from the existing zip file. Existing information contains entry name(file name) and its CRC among other things.
在其他部分也是如此。这会在 zip 文件中创建一个新条目,其中包含来自现有 zip 文件的信息。现有信息包含条目名称(文件名)及其 CRC 等。
And then, when u try to update the text file and close the zip file, it will throw an error as the CRC defined in the entry and the CRC of the object you are trying to write differ.
然后,当您尝试更新文本文件并关闭 zip 文件时,它会抛出错误,因为条目中定义的 CRC 与您尝试写入的对象的 CRC 不同。
Also u might get an error if the length of the text that you are trying to replace is different than the one existing i.e. you are trying to replace
如果您尝试替换的文本长度与现有文本的长度不同,即您尝试替换的文本长度,您也可能会收到错误消息
key1=value1
键 1=值 1
with
和
key1=val1
键 1=值 1
This boils down to the problem that the buffer you are trying to write to has length different than the one specified.
这归结为您尝试写入的缓冲区的长度与指定的长度不同的问题。
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();
The following code ensures that even if data that is replaced is of less length than the original length, no IndexOutOfBoundsExceptions occur.
以下代码确保即使被替换的数据长度小于原始长度,也不会发生 IndexOutOfBoundsExceptions。
(len < buf.length) ? len : buf.length
(len < buf.length) ? len : buf.length
回答by Alex Lipov
Java 7 introduced a much simpler way for doing zip archive manipulations - FileSystemsAPI, which allows to access contents of a file as a file system.
Java 7 引入了一种更简单的 zip 存档操作方法 - FileSystemsAPI,它允许将文件内容作为文件系统访问。
Besides much more straightforward API, it is doing the modification in-place and doesn't require to rewrite other (irrelevant) files in a zip archive (as done in the accepted answer).
除了更直接的 API 之外,它还进行了就地修改,并且不需要重写 zip 存档中的其他(不相关)文件(如已接受的答案中所做的那样)。
Here's sample code that solves OP's use case:
这是解决 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();
}
}
}
For more zip archive manipulation examples, see demo/nio/zipfs/Demo.java
sample which you can download here(look for JDK 8 Demos and Samples).
有关更多 zip 存档操作示例,请参阅demo/nio/zipfs/Demo.java
您可以在此处下载的示例(查找 JDK 8 演示和示例)。
回答by Santiago Ruiz
Only a little improvement to:
只有一点点改进:
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);
}
}
That should be:
那应该是:
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());
}
In order to capture all the occurrences
为了捕捉所有的发生
The reason is that, with the first, you could have "key1" in one read and "=value1" in the next, not being able to capture the occurrence you want to change
原因是,对于第一个,您可以在一次读取中使用“key1”,在下一次读取中使用“=value1”,但无法捕获您想要更改的事件