diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index 3cb59219e4..46b714572d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -64,7 +64,7 @@ public static CompletableFuture exportLogs(Path zipFile, DefaultGameReposi } return CompletableFuture.runAsync(() -> { - try (Zipper zipper = new Zipper(zipFile)) { + try (Zipper zipper = new Zipper(zipFile, true)) { processLogs(runDirectory.resolve("liteconfig"), "*.log", "liteconfig", zipper); processLogs(runDirectory.resolve("logs"), "*.log", "logs", zipper); processLogs(runDirectory, "*.log", "runDirectory", zipper); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java index 51e8220c9f..5499e8ec33 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java @@ -25,6 +25,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipException; @@ -39,13 +41,15 @@ public final class Zipper implements Closeable { private final ZipOutputStream zos; private final byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE]; + private final Set entryNames; public Zipper(Path zipFile) throws IOException { - this(zipFile, StandardCharsets.UTF_8); + this(zipFile, false); } - public Zipper(Path zipFile, Charset encoding) throws IOException { - this.zos = new ZipOutputStream(Files.newOutputStream(zipFile), encoding); + public Zipper(Path zipFile, boolean allowDuplicateEntry) throws IOException { + this.zos = new ZipOutputStream(Files.newOutputStream(zipFile), StandardCharsets.UTF_8); + this.entryNames = allowDuplicateEntry ? new HashSet<>() : null; } private static String normalize(String path) { @@ -57,6 +61,20 @@ private static String normalize(String path) { return path; } + private ZipEntry newEntry(String name) throws IOException { + if (entryNames == null || entryNames.add(name)) + return new ZipEntry(name); + + for (int i = 1; i < 10; i++) { + String newName = name + "." + i; + if (entryNames.add(newName)) { + return new ZipEntry(newName); + } + } + + throw new ZipException("duplicate entry: " + name); + } + private static String resolve(String dir, String file) { if (dir.isEmpty()) return file; if (file.isEmpty()) return dir; @@ -71,7 +89,7 @@ public void close() throws IOException { /** * Compress all the files in sourceDir * - * @param source the file in basePath to be compressed + * @param source the file in basePath to be compressed * @param targetDir the path of the directory in this zip file. */ public void putDirectory(Path source, String targetDir) throws IOException { @@ -81,9 +99,9 @@ public void putDirectory(Path source, String targetDir) throws IOException { /** * Compress all the files in sourceDir * - * @param source the file in basePath to be compressed + * @param source the file in basePath to be compressed * @param targetDir the path of the directory in this zip file. - * @param filter returns false if you do not want that file or directory + * @param filter returns false if you do not want that file or directory */ public void putDirectory(Path source, String targetDir, ExceptionalPredicate filter) throws IOException { String root = normalize(targetDir); @@ -108,7 +126,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th return FileVisitResult.SKIP_SUBTREE; } try { - zos.putNextEntry(new ZipEntry(resolve(root, relativePath) + "/")); + zos.putNextEntry(newEntry(resolve(root, relativePath) + "/")); zos.closeEntry(); } catch (ZipException ignored) { // Directory already exists @@ -118,16 +136,12 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th }); } - public void putFile(File file, String path) throws IOException { - putFile(file.toPath(), path); - } - public void putFile(Path file, String path) throws IOException { path = normalize(path); BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class); - ZipEntry entry = new ZipEntry(attrs.isDirectory() ? path + "/" : path); + ZipEntry entry = newEntry(attrs.isDirectory() ? path + "/" : path); entry.setCreationTime(attrs.creationTime()); entry.setLastAccessTime(attrs.lastAccessTime()); entry.setLastModifiedTime(attrs.lastModifiedTime()); @@ -135,6 +149,7 @@ public void putFile(Path file, String path) throws IOException { if (attrs.isDirectory()) { try { zos.putNextEntry(entry); + entryNames.add(entry.getName()); zos.closeEntry(); } catch (ZipException ignored) { // Directory already exists @@ -149,13 +164,13 @@ public void putFile(Path file, String path) throws IOException { } public void putStream(InputStream in, String path) throws IOException { - zos.putNextEntry(new ZipEntry(normalize(path))); + zos.putNextEntry(newEntry(normalize(path))); IOUtils.copyTo(in, zos, buffer); zos.closeEntry(); } public OutputStream putStream(String path) throws IOException { - zos.putNextEntry(new ZipEntry(normalize(path))); + zos.putNextEntry(newEntry(normalize(path))); return new OutputStream() { public void write(int b) throws IOException { zos.write(b); @@ -180,7 +195,7 @@ public void close() throws IOException { } public void putLines(Stream lines, String path) throws IOException { - zos.putNextEntry(new ZipEntry(normalize(path))); + zos.putNextEntry(newEntry(normalize(path))); try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos)); @@ -205,7 +220,7 @@ public void putTextFile(String text, String path) throws IOException { } public void putTextFile(String text, Charset encoding, String path) throws IOException { - zos.putNextEntry(new ZipEntry(normalize(path))); + zos.putNextEntry(newEntry(normalize(path))); zos.write(text.getBytes(encoding)); zos.closeEntry(); }