From ef417a2c14a49a063e5e69d54dfad57f36a12ca3 Mon Sep 17 00:00:00 2001 From: johnniang Date: Fri, 19 Apr 2019 09:36:20 +0800 Subject: [PATCH] Add some useful features to FileUtils --- .../java/run/halo/app/utils/FileUtils.java | 115 +++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/src/main/java/run/halo/app/utils/FileUtils.java b/src/main/java/run/halo/app/utils/FileUtils.java index 4372665d1..adae39b92 100644 --- a/src/main/java/run/halo/app/utils/FileUtils.java +++ b/src/main/java/run/halo/app/utils/FileUtils.java @@ -2,6 +2,7 @@ package run.halo.app.utils; import lombok.extern.slf4j.Slf4j; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import run.halo.app.exception.ForbiddenException; @@ -11,6 +12,7 @@ import java.io.InputStream; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.Comparator; +import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** @@ -68,6 +70,86 @@ public class FileUtils { .forEach(File::delete); } + /** + * Unzip content to the target path. + * + * @param zis zip input stream must not be null + * @param targetPath target path must not be null and not empty + * @throws IOException + */ + public static void unzip(@NonNull ZipInputStream zis, @NonNull Path targetPath) throws IOException { + Assert.notNull(zis, "Zip input stream must not be null"); + Assert.notNull(targetPath, "Target path must not be null"); + + // Create path if absent + createIfAbsent(targetPath); + + // Must be empty + mustBeEmpty(targetPath); + + ZipEntry zipEntry = zis.getNextEntry(); + + while (zipEntry != null) { + // Resolve the entry path + Path entryPath = targetPath.resolve(zipEntry.getName()); + + // Check directory + FileUtils.checkDirectoryTraversal(targetPath, entryPath); + + if (zipEntry.isDirectory()) { + // Create directories + Files.createDirectories(entryPath); + } else { + // Copy file + Files.copy(zis, entryPath); + } + + zipEntry = zis.getNextEntry(); + } + } + + /** + * Creates directories if absent. + * + * @param path path must not be null + * @throws IOException + */ + public static void createIfAbsent(@NonNull Path path) throws IOException { + Assert.notNull(path, "Path must not be null"); + + if (Files.notExists(path)) { + // Create directories + Files.createDirectories(path); + + log.debug("Created directory: [{}]", path); + } + } + + /** + * Checks if the given path is empty. + * + * @param path path must not be null + * @return true if the given path is empty; false otherwise + * @throws IOException + */ + public static boolean isEmpty(@NonNull Path path) throws IOException { + Assert.notNull(path, "Path must not be null"); + + return Files.list(path).count() == 0; + } + + /** + * The given path must be empty. + * + * @param path path must not be null + * @throws IOException + */ + public static void mustBeEmpty(@NonNull Path path) throws IOException { + if (!isEmpty(path)) { + throw new DirectoryNotEmptyException("Target directory: " + path + " was not empty"); + } + } + /** * Checks directory traversal vulnerability. * @@ -105,18 +187,28 @@ public class FileUtils { throw new ForbiddenException("You cannot access " + pathToCheck).setErrorData(pathToCheck); } - public static void closeQuietly(InputStream inputStream) { + /** + * Closes input stream quietly. + * + * @param inputStream input stream + */ + public static void closeQuietly(@Nullable InputStream inputStream) { try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { // Ignore this exception - log.error("Failed to close input stream", e); + log.warn("Failed to close input stream", e); } } - public static void closeQuietly(ZipInputStream zipInputStream) { + /** + * Closes zip input stream quietly. + * + * @param zipInputStream zip input stream + */ + public static void closeQuietly(@Nullable ZipInputStream zipInputStream) { try { if (zipInputStream != null) { zipInputStream.closeEntry(); @@ -124,7 +216,22 @@ public class FileUtils { } } catch (IOException e) { // Ignore this exception - log.error("Failed to close zip input stream", e); + log.warn("Failed to close zip input stream", e); + } + } + + /** + * Deletes folder quietly. + * + * @param deletingPath deleting path must not be null + */ + public static void deleteFolderQuietly(@NonNull Path deletingPath) { + try { + if (deletingPath != null) { + FileUtils.deleteFolder(deletingPath); + } + } catch (IOException e) { + log.warn("Failed to delete " + deletingPath); } } }