From 612c2d8a98285e1fddf330f91cea99864b993553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <626546063@qq.com> Date: Fri, 15 Oct 2021 01:37:16 +0000 Subject: [PATCH 1/4] add method --- .../java/cn/hutool/core/util/ZipUtil.java | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java index c40ded1cd..064642772 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -9,17 +9,14 @@ import cn.hutool.core.io.FastByteArrayOutputStream; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.FileSystemUtil; import cn.hutool.core.io.resource.Resource; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.nio.charset.Charset; +import java.nio.file.FileSystem; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -75,6 +72,61 @@ public class ZipUtil { } } + /** + * 在zip文件中添加新文件, 如果已经存在则不会有效果 + * + * @param zipFilePathStr zip文件存储路径 + * @param appendFilePathStr 待添加文件路径(可以是文件夹) + */ + public static void addFile(String zipFilePathStr, String appendFilePathStr) throws IOException { + Path zipPath = Paths.get(zipFilePathStr); + Path appendFilePath = Paths.get(appendFilePathStr); + + try (FileSystem zipFileSystem = FileSystemUtil.createZip(zipPath.toString())) { + Path root = zipFileSystem.getPath("/"); + Path dest = zipFileSystem.getPath(root.toString(), appendFilePath.getFileName().toString()); + if (!Files.isDirectory(appendFilePath)) { + Files.copy(appendFilePath, dest, StandardCopyOption.COPY_ATTRIBUTES); + } else { + Files.walkFileTree(appendFilePath, new SimpleFileVisitor() { + /** + * 用于保证文件夹拷贝后的效果跟常见压缩软件的效果相同 + */ + private String dirRoot = null; + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + final Path dest; + if (dirRoot != null) { + dest = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(file.toString(), dirRoot, false)); + } else { + dest = zipFileSystem.getPath(root.toString(), file.toString()); + } + Files.copy(file, dest, StandardCopyOption.COPY_ATTRIBUTES); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + final Path dirToCreate; + if (dirRoot == null) { + dirToCreate = zipFileSystem.getPath(root.toString(), dir.getFileName().toString()); + dirRoot = dir.getFileName().toString(); + } else { + dirToCreate = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(dir.toString(), dirRoot, false)); + } + if (Files.notExists(dirToCreate)) { + Files.createDirectories(dirToCreate); + } + return FileVisitResult.CONTINUE; + } + }); + } + } catch (FileAlreadyExistsException ignored) { + // 文件已存在, 跳过 + } + } + /** * 打包到当前目录,使用默认编码UTF-8 * From c9bb53dd03a93e5265162f36adc905c49c217a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <626546063@qq.com> Date: Mon, 18 Oct 2021 09:58:56 +0000 Subject: [PATCH 2/4] add support for zip file --- .../core/io/file/visitor/CopyVisitor.java | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java index 842dbff1e..e8bd619be 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/visitor/CopyVisitor.java @@ -1,7 +1,11 @@ package cn.hutool.core.io.file.visitor; import cn.hutool.core.io.file.PathUtil; +import cn.hutool.core.util.StrUtil; +import com.sun.nio.zipfs.ZipFileSystem; +import com.sun.nio.zipfs.ZipPath; +import java.io.File; import java.io.IOException; import java.nio.file.CopyOption; import java.nio.file.FileAlreadyExistsException; @@ -23,6 +27,8 @@ public class CopyVisitor extends SimpleFileVisitor { private final Path source; private final Path target; private boolean isTargetCreated; + private final boolean isZipFile; + private String dirRoot = null; private final CopyOption[] copyOptions; /** @@ -38,15 +44,28 @@ public class CopyVisitor extends SimpleFileVisitor { } this.source = source; this.target = target; + this.isZipFile = target instanceof ZipPath; this.copyOptions = copyOptions; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - initTarget(); - // 将当前目录相对于源路径转换为相对于目标路径 - final Path targetDir = target.resolve(source.relativize(dir)); + final Path targetDir; + if (isZipFile) { + ZipPath zipPath = (ZipPath) target; + ZipFileSystem fileSystem = zipPath.getFileSystem(); + if (dirRoot == null) { + targetDir = fileSystem.getPath(dir.getFileName().toString()); + dirRoot = dir.getFileName().toString() + File.separator; + } else { + targetDir = fileSystem.getPath(dirRoot, StrUtil.subAfter(dir.toString(), dirRoot, false)); + } + } else { + initTarget(); + // 将当前目录相对于源路径转换为相对于目标路径 + targetDir = target.resolve(source.relativize(dir)); + } try { Files.copy(dir, targetDir, copyOptions); } catch (FileAlreadyExistsException e) { @@ -59,8 +78,17 @@ public class CopyVisitor extends SimpleFileVisitor { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - initTarget(); - Files.copy(file, target.resolve(source.relativize(file)), copyOptions); + if (isZipFile) { + if (dirRoot == null) { + Files.copy(file, target, copyOptions); + } else { + ZipPath zipPath = (ZipPath) target; + Files.copy(file, zipPath.getFileSystem().getPath(dirRoot, StrUtil.subAfter(file.toString(), dirRoot, false)), copyOptions); + } + } else { + initTarget(); + Files.copy(file, target.resolve(source.relativize(file)), copyOptions); + } return FileVisitResult.CONTINUE; } From b2c5438692c5b56ae435e5c4a1858d603f1b1607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <626546063@qq.com> Date: Wed, 20 Oct 2021 01:56:24 +0000 Subject: [PATCH 3/4] use CopyVisitor --- .../java/cn/hutool/core/util/ZipUtil.java | 51 ++++++------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java index 064642772..dc8d55230 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -10,13 +10,24 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.file.FileSystemUtil; +import cn.hutool.core.io.file.visitor.CopyVisitor; import cn.hutool.core.io.resource.Resource; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.Charset; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystem; -import java.nio.file.*; -import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -88,39 +99,7 @@ public class ZipUtil { if (!Files.isDirectory(appendFilePath)) { Files.copy(appendFilePath, dest, StandardCopyOption.COPY_ATTRIBUTES); } else { - Files.walkFileTree(appendFilePath, new SimpleFileVisitor() { - /** - * 用于保证文件夹拷贝后的效果跟常见压缩软件的效果相同 - */ - private String dirRoot = null; - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - final Path dest; - if (dirRoot != null) { - dest = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(file.toString(), dirRoot, false)); - } else { - dest = zipFileSystem.getPath(root.toString(), file.toString()); - } - Files.copy(file, dest, StandardCopyOption.COPY_ATTRIBUTES); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - final Path dirToCreate; - if (dirRoot == null) { - dirToCreate = zipFileSystem.getPath(root.toString(), dir.getFileName().toString()); - dirRoot = dir.getFileName().toString(); - } else { - dirToCreate = zipFileSystem.getPath(root.toString(), dirRoot, File.separator, StrUtil.subAfter(dir.toString(), dirRoot, false)); - } - if (Files.notExists(dirToCreate)) { - Files.createDirectories(dirToCreate); - } - return FileVisitResult.CONTINUE; - } - }); + Files.walkFileTree(appendFilePath, new CopyVisitor(appendFilePath, zipFileSystem.getPath(zipFilePathStr))); } } catch (FileAlreadyExistsException ignored) { // 文件已存在, 跳过 From fd1f330166cf6196a3407a6d980b8422092d2d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E5=8A=AD=E6=98=8E?= <42922512+shenshaoming@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:43:51 +0800 Subject: [PATCH 4/4] add test --- .../java/cn/hutool/core/util/ZipUtilTest.java | 47 ++++++++++++++++++ .../src/test/resources/test-zip/addFile.txt | 2 + .../test/resources/test-zip/test-add/test.txt | 0 .../src/test/resources/test-zip/test.zip | Bin 0 -> 150 bytes 4 files changed, 49 insertions(+) create mode 100644 hutool-core/src/test/resources/test-zip/addFile.txt create mode 100644 hutool-core/src/test/resources/test-zip/test-add/test.txt create mode 100644 hutool-core/src/test/resources/test-zip/test.zip diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java index 0246f6688..acd389ab1 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ZipUtilTest.java @@ -1,5 +1,6 @@ package cn.hutool.core.util; +import cn.hutool.core.compress.ZipReader; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.lang.Console; @@ -12,6 +13,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; /** * {@link ZipUtil}单元测试 @@ -20,6 +23,50 @@ import java.nio.charset.Charset; */ public class ZipUtilTest { + @Test + public void addFileTest() throws IOException { + File appendFile = FileUtil.file("test-zip/addFile.txt"); + File zipFile = FileUtil.file("test-zip/test.zip"); + + // 用于测试完成后将被测试文件恢复 + File tempZipFile = FileUtil.createTempFile(FileUtil.file("test-zip")); + tempZipFile.deleteOnExit(); + FileUtil.copy(zipFile, tempZipFile, true); + + // test file add + List beforeNames = zipEntryNames(zipFile); + ZipUtil.addFile(zipFile.getAbsolutePath(), appendFile.getAbsolutePath()); + List afterNames = zipEntryNames(zipFile); + Assert.assertTrue(afterNames.containsAll(beforeNames)); + Assert.assertTrue(afterNames.contains(appendFile.getName())); + + // test dir add + beforeNames = afterNames; + File addDirFile = FileUtil.file("test-zip/test-add"); + ZipUtil.addFile(zipFile.getAbsolutePath(), addDirFile.getAbsolutePath()); + afterNames = zipEntryNames(zipFile); + + Assert.assertTrue(afterNames.containsAll(beforeNames)); + Assert.assertTrue(afterNames.contains(appendFile.getName())); + + // rollback + FileUtil.copy(tempZipFile, zipFile, true); + Assert.assertTrue(String.format("delete temp file %s failed", tempZipFile.getCanonicalPath()), tempZipFile.delete()); + } + + /** + * 获取zip文件中所有一级文件/文件夹的name + * + * @param zipFile 待测试的zip文件 + * @return zip文件中一级目录下的所有文件/文件夹名 + */ + private List zipEntryNames(File zipFile) { + List fileNames = new ArrayList<>(); + ZipReader reader = ZipReader.of(zipFile, CharsetUtil.CHARSET_UTF_8); + reader.read(zipEntry -> fileNames.add(zipEntry.getName())); + reader.close(); + return fileNames; + } @Test @Ignore diff --git a/hutool-core/src/test/resources/test-zip/addFile.txt b/hutool-core/src/test/resources/test-zip/addFile.txt new file mode 100644 index 000000000..8d1c2fee6 --- /dev/null +++ b/hutool-core/src/test/resources/test-zip/addFile.txt @@ -0,0 +1,2 @@ +this file will be used to add into the test.zip +before the add action, the test.zip won't have this file. diff --git a/hutool-core/src/test/resources/test-zip/test-add/test.txt b/hutool-core/src/test/resources/test-zip/test-add/test.txt new file mode 100644 index 000000000..e69de29bb diff --git a/hutool-core/src/test/resources/test-zip/test.zip b/hutool-core/src/test/resources/test-zip/test.zip new file mode 100644 index 0000000000000000000000000000000000000000..86126d555dc839601f5165c532e08c4260fa115b GIT binary patch literal 150 zcmWIWW@h1H0D(~dkYF$aN^k(_lGNf7y^@NO0B=Sndj?$URG>N(pfpSu7Xw6ukwJo? dw0#d_*wO2Z=yZTLD;r1~BM?RbX*Cds0RRL47l{A> literal 0 HcmV?d00001