[英]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.