From 81b0e95500a055bcb418dfd03cbceecf4f5bb061 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 5 Mar 2023 21:04:04 +0800 Subject: [PATCH] remove FileCopier --- .../main/java/cn/hutool/core/io/FileUtil.java | 88 ++--- .../cn/hutool/core/io/file/FileCopier.java | 313 ------------------ .../cn/hutool/core/io/file/PathCopier.java | 165 +++++++++ .../cn/hutool/core/io/file/PathMover.java | 9 +- .../java/cn/hutool/core/io/file/PathUtil.java | 92 ++--- .../core/lang/copier/SrcToDestCopier.java | 12 +- .../cn/hutool/core/io/FileCopierTest.java | 59 ---- .../java/cn/hutool/core/io/FileUtilTest.java | 9 - .../cn/hutool/core/io/file/PathCopyTest.java | 79 +++++ .../cn/hutool/core/io/file/PathUtilTest.java | 2 +- 10 files changed, 296 insertions(+), 532 deletions(-) delete mode 100755 hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java create mode 100755 hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java delete mode 100644 hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java create mode 100755 hutool-core/src/test/java/cn/hutool/core/io/file/PathCopyTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index b41fbbd86..78073629d 100755 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -44,11 +44,7 @@ import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.nio.charset.Charset; -import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.nio.file.*; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -984,43 +980,6 @@ public class FileUtil extends PathUtil { } } - /** - * 通过JDK7+的 Files#copy(Path, Path, CopyOption...) 方法拷贝文件 - * - * @param src 源文件路径 - * @param dest 目标文件或目录路径,如果为目录使用与源文件相同的文件名 - * @param options {@link StandardCopyOption} - * @return File - * @throws IORuntimeException IO异常 - */ - public static File copyFile(final String src, final String dest, final StandardCopyOption... options) throws IORuntimeException { - Assert.notBlank(src, "Source File path is blank !"); - Assert.notBlank(dest, "Destination File path is blank !"); - return copyFile(Paths.get(src), Paths.get(dest), options).toFile(); - } - - /** - * 通过JDK7+的 Files#copy(Path, Path, CopyOption...) 方法拷贝文件 - * - * @param src 源文件 - * @param dest 目标文件或目录,如果为目录使用与源文件相同的文件名 - * @param options {@link StandardCopyOption} - * @return 目标文件 - * @throws IORuntimeException IO异常 - */ - public static File copyFile(final File src, final File dest, final StandardCopyOption... options) throws IORuntimeException { - // check - Assert.notNull(src, "Source File is null !"); - if (false == src.exists()) { - throw new IORuntimeException("File not exist: " + src); - } - Assert.notNull(dest, "Destination File or directiory is null !"); - if (equals(src, dest)) { - throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest); - } - return copyFile(src.toPath(), dest.toPath(), options).toFile(); - } - /** * 复制文件或目录
* 如果目标文件为目录,则将源文件以相同文件名拷贝到目标目录 @@ -1046,13 +1005,19 @@ public class FileUtil extends PathUtil { * * * @param src 源文件 - * @param dest 目标文件或目录,目标不存在会自动创建(目录、文件都创建) + * @param target 目标文件或目录,目标不存在会自动创建(目录、文件都创建) * @param isOverride 是否覆盖目标文件 * @return 目标目录或文件 * @throws IORuntimeException IO异常 */ - public static File copy(final File src, final File dest, final boolean isOverride) throws IORuntimeException { - return FileCopier.of(src, dest).setOverride(isOverride).copy(); + public static File copy(final File src, final File target, final boolean isOverride) throws IORuntimeException { + Assert.notNull(src, "Src file must be not null!"); + Assert.notNull(target, "target file must be not null!"); + return PathUtil.copy( + src.toPath(), + target.toPath(), + isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}) + .toFile(); } /** @@ -1066,34 +1031,19 @@ public class FileUtil extends PathUtil { * * * @param src 源文件 - * @param dest 目标文件或目录,目标不存在会自动创建(目录、文件都创建) + * @param target 目标文件或目录,目标不存在会自动创建(目录、文件都创建) * @param isOverride 是否覆盖目标文件 * @return 目标目录或文件 * @throws IORuntimeException IO异常 */ - public static File copyContent(final File src, final File dest, final boolean isOverride) throws IORuntimeException { - return FileCopier.of(src, dest).setCopyContentIfDir(true).setOverride(isOverride).copy(); - } - - /** - * 复制文件或目录
- * 情况如下: - * - *
-	 * 1、src和dest都为目录,则将src下所有文件(包括子目录)拷贝到dest下
-	 * 2、src和dest都为文件,直接复制,名字为dest
-	 * 3、src为文件,dest为目录,将src拷贝到dest目录下
-	 * 
- * - * @param src 源文件 - * @param dest 目标文件或目录,目标不存在会自动创建(目录、文件都创建) - * @param isOverride 是否覆盖目标文件 - * @return 目标目录或文件 - * @throws IORuntimeException IO异常 - * @since 4.1.5 - */ - public static File copyFilesFromDir(final File src, final File dest, final boolean isOverride) throws IORuntimeException { - return FileCopier.of(src, dest).setCopyContentIfDir(true).setOnlyCopyFile(true).setOverride(isOverride).copy(); + public static File copyContent(final File src, final File target, final boolean isOverride) throws IORuntimeException { + Assert.notNull(src, "Src file must be not null!"); + Assert.notNull(target, "target file must be not null!"); + return PathUtil.copyContent( + src.toPath(), + target.toPath(), + isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}) + .toFile(); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java deleted file mode 100755 index 7ced5f670..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java +++ /dev/null @@ -1,313 +0,0 @@ -package cn.hutool.core.io.file; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.IORuntimeException; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.copier.SrcToDestCopier; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.text.StrUtil; - -import java.io.File; -import java.io.IOException; -import java.nio.file.CopyOption; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; - -/** - * 文件拷贝器
- * 支持以下几种情况: - *
- * 1、文件复制到文件
- * 2、文件复制到目录
- * 3、目录复制到目录
- * 4、目录下的文件和目录复制到另一个目录
- * 
- * - * @author Looly - * @since 3.0.9 - */ -public class FileCopier extends SrcToDestCopier { - private static final long serialVersionUID = 1L; - - /** - * 是否覆盖目标文件 - */ - private boolean isOverride; - /** - * 是否拷贝所有属性 - */ - private boolean isCopyAttributes; - /** - * 当拷贝来源是目录时是否只拷贝目录下的内容 - */ - private boolean isCopyContentIfDir; - /** - * 当拷贝来源是目录时是否只拷贝文件而忽略子目录 - */ - private boolean isOnlyCopyFile; - - //-------------------------------------------------------------------------------------------------------- static method start - - /** - * 新建一个文件复制器 - * - * @param srcPath 源文件路径(相对ClassPath路径或绝对路径) - * @param destPath 目标文件路径(相对ClassPath路径或绝对路径) - * @return this - */ - public static FileCopier of(final String srcPath, final String destPath) { - return new FileCopier(FileUtil.file(srcPath), FileUtil.file(destPath)); - } - - /** - * 新建一个文件复制器 - * - * @param src 源文件 - * @param dest 目标文件 - * @return this - */ - public static FileCopier of(final File src, final File dest) { - return new FileCopier(src, dest); - } - //-------------------------------------------------------------------------------------------------------- static method end - - //-------------------------------------------------------------------------------------------------------- Constructor start - - /** - * 构造 - * - * @param src 源文件 - * @param dest 目标文件 - */ - public FileCopier(final File src, final File dest) { - this.src = src; - this.dest = dest; - } - //-------------------------------------------------------------------------------------------------------- Constructor end - - //-------------------------------------------------------------------------------------------------------- Getters and Setters start - - /** - * 是否覆盖目标文件 - * - * @return 是否覆盖目标文件 - */ - public boolean isOverride() { - return isOverride; - } - - /** - * 设置是否覆盖目标文件 - * - * @param isOverride 是否覆盖目标文件 - * @return this - */ - public FileCopier setOverride(final boolean isOverride) { - this.isOverride = isOverride; - return this; - } - - /** - * 是否拷贝所有属性 - * - * @return 是否拷贝所有属性 - */ - public boolean isCopyAttributes() { - return isCopyAttributes; - } - - /** - * 设置是否拷贝所有属性 - * - * @param isCopyAttributes 是否拷贝所有属性 - * @return this - */ - public FileCopier setCopyAttributes(final boolean isCopyAttributes) { - this.isCopyAttributes = isCopyAttributes; - return this; - } - - /** - * 当拷贝来源是目录时是否只拷贝目录下的内容 - * - * @return 当拷贝来源是目录时是否只拷贝目录下的内容 - */ - public boolean isCopyContentIfDir() { - return isCopyContentIfDir; - } - - /** - * 当拷贝来源是目录时是否只拷贝目录下的内容 - * - * @param isCopyContentIfDir 是否只拷贝目录下的内容 - * @return this - */ - public FileCopier setCopyContentIfDir(final boolean isCopyContentIfDir) { - this.isCopyContentIfDir = isCopyContentIfDir; - return this; - } - - /** - * 当拷贝来源是目录时是否只拷贝文件而忽略子目录 - * - * @return 当拷贝来源是目录时是否只拷贝文件而忽略子目录 - * @since 4.1.5 - */ - public boolean isOnlyCopyFile() { - return isOnlyCopyFile; - } - - /** - * 设置当拷贝来源是目录时是否只拷贝文件而忽略子目录 - * - * @param isOnlyCopyFile 当拷贝来源是目录时是否只拷贝文件而忽略子目录 - * @return this - * @since 4.1.5 - */ - public FileCopier setOnlyCopyFile(final boolean isOnlyCopyFile) { - this.isOnlyCopyFile = isOnlyCopyFile; - return this; - } - //-------------------------------------------------------------------------------------------------------- Getters and Setters end - - /** - * 执行拷贝
- * 拷贝规则为: - *
-	 * 1、源为文件,目标为已存在目录,则拷贝到目录下,文件名不变
-	 * 2、源为文件,目标为不存在路径,则目标以文件对待(自动创建父级目录)比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
-	 * 3、源为文件,目标是一个已存在的文件,则当{@link #setOverride(boolean)}设为true时会被覆盖,默认不覆盖
-	 * 4、源为目录,目标为已存在目录,当{@link #setCopyContentIfDir(boolean)}为true时,只拷贝目录中的内容到目标目录中,否则整个源目录连同其目录拷贝到目标目录中
-	 * 5、源为目录,目标为不存在路径,则自动创建目标为新目录,然后按照规则4复制
-	 * 6、源为目录,目标为文件,抛出IO异常
-	 * 7、源路径和目标路径相同时,抛出IO异常
-	 * 
- * - * @return 拷贝后目标的文件或目录 - * @throws IORuntimeException IO异常 - */ - @Override - public File copy() throws IORuntimeException { - final File src = this.src; - File dest = this.dest; - // check - Assert.notNull(src, "Source File is null !"); - if (false == src.exists()) { - throw new IORuntimeException("File not exist: " + src); - } - Assert.notNull(dest, "Destination File or directiory is null !"); - if (FileUtil.equals(src, dest)) { - throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest); - } - - if (src.isDirectory()) {// 复制目录 - if (dest.exists() && false == dest.isDirectory()) { - //源为目录,目标为文件,抛出IO异常 - throw new IORuntimeException("Src is a directory but dest is a file!"); - } - if (FileUtil.isSub(src, dest)) { - throw new IORuntimeException("Dest is a sub directory of src !"); - } - - final File subTarget = isCopyContentIfDir ? dest : FileUtil.mkdir(FileUtil.file(dest, src.getName())); - internalCopyDirContent(src, subTarget); - } else {// 复制文件 - dest = internalCopyFile(src, dest); - } - return dest; - } - - //----------------------------------------------------------------------------------------- Private method start - - /** - * 拷贝目录内容,只用于内部,不做任何安全检查
- * 拷贝内容的意思为源目录下的所有文件和目录拷贝到另一个目录下,而不拷贝源目录本身 - * - * @param src 源目录 - * @param dest 目标目录 - * @throws IORuntimeException IO异常 - */ - private void internalCopyDirContent(final File src, final File dest) throws IORuntimeException { - if (null != copyPredicate && false == copyPredicate.test(src)) { - //被过滤的目录跳过 - return; - } - - if (false == dest.exists()) { - //目标为不存在路径,创建为目录 - //noinspection ResultOfMethodCallIgnored - dest.mkdirs(); - } else if (false == dest.isDirectory()) { - throw new IORuntimeException(StrUtil.format("Src [{}] is a directory but dest [{}] is a file!", src.getPath(), dest.getPath())); - } - - final String[] files = src.list(); - if (ArrayUtil.isNotEmpty(files)) { - File srcFile; - File destFile; - for (final String file : files) { - srcFile = new File(src, file); - destFile = this.isOnlyCopyFile ? dest : new File(dest, file); - // 递归复制 - if (srcFile.isDirectory()) { - internalCopyDirContent(srcFile, destFile); - } else { - internalCopyFile(srcFile, destFile); - } - } - } - } - - /** - * 拷贝文件,只用于内部,不做任何安全检查
- * 情况如下: - *
-	 * 1、如果目标是一个不存在的路径,则目标以文件对待(自动创建父级目录)比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
-	 * 2、如果目标是一个已存在的目录,则文件拷贝到此目录下,文件名与原文件名一致
-	 * 
- * - * @param src 源文件,必须为文件 - * @param dest 目标文件,如果非覆盖模式必须为目录 - * @return 目标文件 - * @throws IORuntimeException IO异常 - */ - private File internalCopyFile(final File src, File dest) throws IORuntimeException { - if (null != copyPredicate && false == copyPredicate.test(src)) { - //被过滤的文件跳过 - return dest; - } - - // 如果已经存在目标文件,切为不覆盖模式,跳过之 - if (dest.exists()) { - if (dest.isDirectory()) { - //目标为目录,目录下创建同名文件 - dest = new File(dest, src.getName()); - } - - if (dest.exists() && false == isOverride) { - //非覆盖模式跳过 - return dest; - } - } else { - //路径不存在则创建父目录 - FileUtil.mkParentDirs(dest); - } - - final ArrayList optionList = new ArrayList<>(2); - if (isOverride) { - optionList.add(StandardCopyOption.REPLACE_EXISTING); - } - if (isCopyAttributes) { - optionList.add(StandardCopyOption.COPY_ATTRIBUTES); - } - - try { - Files.copy(src.toPath(), dest.toPath(), optionList.toArray(new CopyOption[0])); - } catch (final IOException e) { - throw new IORuntimeException(e); - } - return dest; - } - //----------------------------------------------------------------------------------------- Private method end -} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java new file mode 100755 index 000000000..99e27bcbe --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathCopier.java @@ -0,0 +1,165 @@ +package cn.hutool.core.io.file; + +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.file.visitor.CopyVisitor; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.copier.SrcToDestCopier; +import cn.hutool.core.util.ObjUtil; + +import java.io.IOException; +import java.nio.file.*; + +/** + * 文件复制封装 + * + * @author looly + * @since 6.0.0 + */ +public class PathCopier extends SrcToDestCopier { + private static final long serialVersionUID = 1L; + + /** + * 创建文件或目录拷贝器 + * + * @param src 源文件或目录 + * @param target 目标文件或目录 + * @param isOverride 是否覆盖目标文件 + * @return {@code PathCopier} + */ + public static PathCopier of(final Path src, final Path target, final boolean isOverride) { + return of(src, target, isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{}); + } + + /** + * 创建文件或目录拷贝器 + * + * @param src 源文件或目录 + * @param target 目标文件或目录 + * @param options 拷贝参数 + * @return {@code PathCopier} + */ + public static PathCopier of(final Path src, final Path target, final CopyOption[] options) { + return new PathCopier(src, target, options); + } + + private final CopyOption[] options; + + /** + * 构造 + * + * @param src 源文件或目录,不能为{@code null}且必须存在 + * @param target 目标文件或目录 + * @param options 移动参数 + */ + public PathCopier(final Path src, final Path target, final CopyOption[] options) { + Assert.notNull(target, "Src path must be not null !"); + if (false == PathUtil.exists(src, false)) { + throw new IllegalArgumentException("Src path is not exist!"); + } + this.src = src; + this.target = Assert.notNull(target, "Target path must be not null !"); + this.options = ObjUtil.defaultIfNull(options, new CopyOption[]{}); + ; + } + + /** + * 复制src到target中 + *
    + *
  • src路径和target路径相同时,不执行操作
  • + *
  • src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
  • + *
  • src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
  • + *
  • src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
  • + *
  • src为目录,target为已存在目录,整个src目录连同其目录拷贝到目标目录中
  • + *
  • src为目录,target为不存在路径,则自动创建目标为新目录,并只拷贝src内容到目标目录中,相当于重命名目录。
  • + *
  • src为目录,target为文件,抛出{@link IllegalArgumentException}
  • + *
+ * + * @return 目标Path + * @throws IORuntimeException IO异常 + */ + @Override + public Path copy() throws IORuntimeException { + if (PathUtil.isDirectory(src)) { + if (PathUtil.exists(target, false)) { + if (PathUtil.isDirectory(target)) { + return _copyContent(src, target.resolve(src.getFileName()), options); + } else { + // src目录,target文件,无法拷贝 + throw new IllegalArgumentException("Can not copy directory to a file!"); + } + } else { + // 目标不存在,按照重命名对待 + return _copyContent(src, target, options); + } + } + return copyFile(src, target, options); + } + + /** + * 复制src的内容到target中 + *
    + *
  • src路径和target路径相同时,不执行操作
  • + *
  • src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
  • + *
  • src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
  • + *
  • src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
  • + *
  • src为目录,target为已存在目录,整个src目录下的内容拷贝到目标目录中
  • + *
  • src为目录,target为不存在路径,则自动创建目标为新目录,整个src目录下的内容拷贝到目标目录中,相当于重命名目录。
  • + *
  • src为目录,target为文件,抛出IO异常
  • + *
+ * + * @return 目标Path + * @throws IORuntimeException IO异常 + */ + public Path copyContent() throws IORuntimeException { + if (PathUtil.isDirectory(src, false)) { + return _copyContent(src, target, options); + } + return copyFile(src, target, options); + } + + /** + * 拷贝目录下的所有文件或目录到目标目录中,此方法不支持文件对文件的拷贝。 + *
    + *
  • 源文件为目录,目标也为目录或不存在,则拷贝目录下所有文件和目录到目标目录下
  • + *
  • 源文件为文件,目标为目录或不存在,则拷贝文件到目标目录下
  • + *
+ * + * @param src 源文件路径,如果为目录只在目标中创建新目录 + * @param target 目标目录,如果为目录使用与源文件相同的文件名 + * @param options {@link StandardCopyOption} + * @return Path + * @throws IORuntimeException IO异常 + */ + private static Path _copyContent(final Path src, final Path target, final CopyOption... options) throws IORuntimeException { + try { + Files.walkFileTree(src, new CopyVisitor(src, target, options)); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + return target; + } + + /** + * 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
+ * 此方法不支持递归拷贝目录,如果src传入是目录,只会在目标目录中创建空目录 + * + * @param src 源文件路径,如果为目录只在目标中创建新目录 + * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名 + * @param options {@link StandardCopyOption} + * @return Path + * @throws IORuntimeException IO异常 + */ + private static Path copyFile(final Path src, final Path target, final CopyOption... options) throws IORuntimeException { + Assert.notNull(src, "Source File is null !"); + Assert.notNull(target, "Destination File or directory is null !"); + + final Path targetPath = PathUtil.isDirectory(target) ? target.resolve(src.getFileName()) : target; + // 创建级联父目录 + PathUtil.mkParentDirs(targetPath); + try { + return Files.copy(src, targetPath, options); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java index 4427b41dd..44de9c576 100755 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathMover.java @@ -1,6 +1,5 @@ package cn.hutool.core.io.file; -import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.file.visitor.MoveVisitor; import cn.hutool.core.lang.Assert; @@ -117,21 +116,21 @@ public class PathMover { *
  • 如果src为文件,target为文件,则按照是否覆盖参数执行。
  • *
  • 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如moveContent("/a/b", "/c/d"), d不存在,则b变成d。
  • *
  • 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
  • - *
  • 如果src为目录,target为目录,则将源目录下的内容移动到目标路径目录中。
  • - *
  • 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录下的内容移动到目标路径目录中。
  • + *
  • 如果src为目录,target为目录,则将源目录下的内容移动到目标路径目录中,源目录不删除。
  • + *
  • 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录下的内容移动到目标路径目录中,源目录不删除。
  • * * * @return 目标文件Path */ public Path moveContent() { final Path src = this.src; - if (PathUtil.isNotDirectory(target, false)) { + if (PathUtil.isExistsAndNotDirectory(target, false)) { // 文件移动调用move方法 return move(); } final Path target = this.target; - if (PathUtil.isNotDirectory(target, false)) { + if (PathUtil.isExistsAndNotDirectory(target, false)) { // 目标不能为文件 throw new IllegalArgumentException("Can not move dir content to a file"); } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java index 8a476d37d..d1f6a65d8 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java @@ -2,7 +2,6 @@ package cn.hutool.core.io.file; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; -import cn.hutool.core.io.file.visitor.CopyVisitor; import cn.hutool.core.io.file.visitor.DelVisitor; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.CharsetUtil; @@ -150,51 +149,15 @@ public class PathUtil { } /** - * 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
    - * 此方法不支持递归拷贝目录,如果src传入是目录,只会在目标目录中创建空目录 - * - * @param src 源文件路径,如果为目录只在目标中创建新目录 - * @param dest 目标文件或目录,如果为目录使用与源文件相同的文件名 - * @param options {@link StandardCopyOption} - * @return Path - * @throws IORuntimeException IO异常 - */ - public static Path copyFile(final Path src, final Path dest, final StandardCopyOption... options) throws IORuntimeException { - return copyFile(src, dest, (CopyOption[]) options); - } - - /** - * 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
    - * 此方法不支持递归拷贝目录,如果src传入是目录,只会在目标目录中创建空目录 - * - * @param src 源文件路径,如果为目录只在目标中创建新目录 - * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名 - * @param options {@link StandardCopyOption} - * @return Path - * @throws IORuntimeException IO异常 - * @since 5.4.1 - */ - public static Path copyFile(final Path src, final Path target, final CopyOption... options) throws IORuntimeException { - Assert.notNull(src, "Source File is null !"); - Assert.notNull(target, "Destination File or directory is null !"); - - final Path targetPath = isDirectory(target) ? target.resolve(src.getFileName()) : target; - // 创建级联父目录 - mkParentDirs(targetPath); - try { - return Files.copy(src, targetPath, options); - } catch (final IOException e) { - throw new IORuntimeException(e); - } - } - - /** - * 拷贝文件或目录,拷贝规则为: - * + * 复制src到target中 *
      - *
    • 源文件为目录,目标也为目录或不存在,则拷贝整个目录到目标目录下
    • - *
    • 源文件为文件,目标为目录或不存在,则拷贝文件到目标目录下
    • - *
    • 源文件为文件,目标也为文件,则在{@link StandardCopyOption#REPLACE_EXISTING}情况下覆盖之
    • + *
    • src路径和target路径相同时,不执行操作
    • + *
    • src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
    • + *
    • src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
    • + *
    • src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
    • + *
    • src为目录,target为已存在目录,整个src目录连同其目录拷贝到目标目录中
    • + *
    • src为目录,target为不存在路径,则自动创建目标为新目录,并只拷贝src内容到目标目录中,相当于重命名目录。
    • + *
    • src为目录,target为文件,抛出{@link IllegalArgumentException}
    • *
    * * @param src 源文件路径,如果为目录会在目标中创建新目录 @@ -202,23 +165,21 @@ public class PathUtil { * @param options {@link StandardCopyOption} * @return Path * @throws IORuntimeException IO异常 - * @since 5.5.1 */ public static Path copy(final Path src, final Path target, final CopyOption... options) throws IORuntimeException { - Assert.notNull(src, "Src path must be not null !"); - Assert.notNull(target, "Target path must be not null !"); - - if (isDirectory(src)) { - return copyContent(src, target.resolve(src.getFileName()), options); - } - return copyFile(src, target, options); + return PathCopier.of(src, target, options).copy(); } /** - * 拷贝目录下的所有文件或目录到目标目录中,此方法不支持文件对文件的拷贝。 + * 复制src的内容到target中 *
      - *
    • 源文件为目录,目标也为目录或不存在,则拷贝目录下所有文件和目录到目标目录下
    • - *
    • 源文件为文件,目标为目录或不存在,则拷贝文件到目标目录下
    • + *
    • src路径和target路径相同时,不执行操作
    • + *
    • src为文件,target为已存在目录,则拷贝到目录下,文件名不变。
    • + *
    • src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录),相当于拷贝后重命名,比如:/dest/aaa,如果aaa不存在,则aaa被当作文件名
    • + *
    • src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖,抛出{@link FileAlreadyExistsException}
    • + *
    • src为目录,target为已存在目录,整个src目录下的内容拷贝到目标目录中
    • + *
    • src为目录,target为不存在路径,则自动创建目标为新目录,整个src目录下的内容拷贝到目标目录中,相当于重命名目录。
    • + *
    • src为目录,target为文件,抛出IO异常
    • *
    * * @param src 源文件路径,如果为目录只在目标中创建新目录 @@ -226,18 +187,9 @@ public class PathUtil { * @param options {@link StandardCopyOption} * @return Path * @throws IORuntimeException IO异常 - * @since 5.5.1 */ public static Path copyContent(final Path src, final Path target, final CopyOption... options) throws IORuntimeException { - Assert.notNull(src, "Src path must be not null !"); - Assert.notNull(target, "Target path must be not null !"); - - try { - Files.walkFileTree(src, new CopyVisitor(src, target, options)); - } catch (final IOException e) { - throw new IORuntimeException(e); - } - return target; + return PathCopier.of(src, target, options).copyContent(); } /** @@ -253,7 +205,7 @@ public class PathUtil { } /** - * 判断是否为非目录 + * 判断是否存在且为非目录 *
      *
    • 如果path为{@code null},返回{@code false}
    • *
    • 如果path不存在,返回{@code false}
    • @@ -264,7 +216,7 @@ public class PathUtil { * @return 如果为目录true * @since 3.1.0 */ - public static boolean isNotDirectory(final Path path, final boolean isFollowLinks) { + public static boolean isExistsAndNotDirectory(final Path path, final boolean isFollowLinks) { return exists(path, isFollowLinks) && false == isDirectory(path, isFollowLinks); } @@ -495,8 +447,8 @@ public class PathUtil { *
    • 如果src为文件,target为文件,则按照是否覆盖参数执行。
    • *
    • 如果src为文件,target为不存在的路径,则重命名源文件到目标指定的文件,如moveContent("/a/b", "/c/d"), d不存在,则b变成d。
    • *
    • 如果src为目录,target为文件,抛出{@link IllegalArgumentException}
    • - *
    • 如果src为目录,target为目录,则将源目录下的内容移动到目标路径目录中。
    • - *
    • 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录下的内容移动到目标路径目录中。
    • + *
    • 如果src为目录,target为目录,则将源目录下的内容移动到目标路径目录中,源目录不删除。
    • + *
    • 如果src为目录,target为不存在的路径,则创建目标路径为目录,将源目录下的内容移动到目标路径目录中,源目录不删除。
    • *
    * * @param src 源文件或目录路径 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java b/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java index 030eb1494..ec143686f 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/copier/SrcToDestCopier.java @@ -19,7 +19,7 @@ public abstract class SrcToDestCopier> implem /** 源 */ protected T src; /** 目标 */ - protected T dest; + protected T target; /** 拷贝过滤器,可以过滤掉不需要拷贝的源 */ protected Predicate copyPredicate; @@ -48,18 +48,18 @@ public abstract class SrcToDestCopier> implem * * @return 目标 */ - public T getDest() { - return dest; + public T getTarget() { + return target; } /** * 设置目标 * - * @param dest 目标 + * @param target 目标 * @return this */ @SuppressWarnings("unchecked") - public C setDest(final T dest) { - this.dest = dest; + public C setTarget(final T target) { + this.target = target; return (C)this; } diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java deleted file mode 100644 index 32b734028..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.hutool.core.io; - -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; - -import cn.hutool.core.io.file.FileCopier; - -import java.io.File; - -/** - * 文件拷贝单元测试 - * - * @author Looly - */ -public class FileCopierTest { - - @Test - @Ignore - public void dirCopyTest() { - final FileCopier copier = FileCopier.of("D:\\Java", "e:/eclipse/eclipse2.zip"); - copier.copy(); - } - - @Test - @Ignore - public void dirCopyTest2() { - //测试带.的文件夹复制 - final FileCopier copier = FileCopier.of("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); - copier.copy(); - - FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true); - } - - @Test(expected = IORuntimeException.class) - public void dirCopySubTest() { - //测试父目录复制到子目录报错 - final FileCopier copier = FileCopier.of("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); - copier.copy(); - } - - @Test - @Ignore - public void copyFileToDirTest() { - final FileCopier copier = FileCopier.of("d:/GReen_Soft/XshellXftpPortable.zip", "c:/hp/"); - copier.copy(); - } - - @Test - @Ignore - public void copyFileByRelativePath(){ - // https://github.com/dromara/hutool/pull/2188 - // 当复制的目标文件位置是相对路径的时候可以通过 - final FileCopier copier = FileCopier.of(new File("pom.xml"),new File("aaa.txt")); - copier.copy(); - final boolean delete = new File("aaa.txt").delete(); - Assert.assertTrue(delete); - } -} diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java index d0db01e8c..29d764e2c 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java @@ -92,15 +92,6 @@ public class FileUtilTest { Assert.assertEquals(srcFile.length(), destFile.length()); } - @Test - @Ignore - public void copyFilesFromDirTest() { - final File srcFile = FileUtil.file("D:\\驱动"); - final File destFile = FileUtil.file("d:\\驱动备份"); - - FileUtil.copyFilesFromDir(srcFile, destFile, true); - } - @Test @Ignore public void copyDirTest() { diff --git a/hutool-core/src/test/java/cn/hutool/core/io/file/PathCopyTest.java b/hutool-core/src/test/java/cn/hutool/core/io/file/PathCopyTest.java new file mode 100755 index 000000000..bd31ba3df --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/io/file/PathCopyTest.java @@ -0,0 +1,79 @@ +package cn.hutool.core.io.file; + +import org.junit.Test; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * 文件或目录拷贝测试 + */ +public class PathCopyTest { + + @Test + public void copySameFileTest() { + final Path path = Paths.get("d:/test/dir1/test1.txt"); + //src路径和target路径相同时,不执行操作 + PathUtil.copy( + path, + path); + } + + @Test + public void copySameDirTest() { + final Path path = Paths.get("d:/test/dir1"); + //src路径和target路径相同时,不执行操作 + PathUtil.copyContent( + path, + path); + } + + @Test + public void copyFileToDirTest() { + // src为文件,target为已存在目录,则拷贝到目录下,文件名不变。 + PathUtil.copy( + Paths.get("d:/test/dir1/test1.txt"), + Paths.get("d:/test/dir2")); + } + + @Test + public void copyFileToPathNotExistTest() { + // src为文件,target为不存在路径,则目标以文件对待(自动创建父级目录) + // 相当于拷贝后重命名 + PathUtil.copy( + Paths.get("d:/test/dir1/test1.txt"), + Paths.get("d:/test/test2")); + } + + @Test + public void copyFileToFileTest() { + //src为文件,target是一个已存在的文件,则当{@link CopyOption}设为覆盖时会被覆盖,默认不覆盖。 + PathUtil.copy( + Paths.get("d:/test/dir1/test1.txt"), + Paths.get("d:/test/test2")); + } + + @Test + public void copyDirToDirTest() { + //src为目录,target为已存在目录,整个src目录连同其目录拷贝到目标目录中 + PathUtil.copy( + Paths.get("d:/test/dir1/"), + Paths.get("d:/test/dir2")); + } + + @Test + public void copyDirToPathNotExistTest() { + //src为目录,target为不存在路径,则自动创建目标为新目录,整个src目录连同其目录拷贝到目标目录中 + PathUtil.copy( + Paths.get("d:/test/dir1/"), + Paths.get("d:/test/dir3")); + } + + @Test + public void copyDirToFileTest() { + //src为目录,target为文件,抛出IllegalArgumentException + PathUtil.copy( + Paths.get("d:/test/dir1/"), + Paths.get("d:/test/exist.txt")); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java index 1f960aa92..364d70051 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/file/PathUtilTest.java @@ -13,7 +13,7 @@ public class PathUtilTest { @Test @Ignore public void copyFileTest(){ - PathUtil.copyFile( + PathUtil.copy( Paths.get("d:/test/1595232240113.jpg"), Paths.get("d:/test/1595232240113_copy.jpg"), StandardCopyOption.COPY_ATTRIBUTES,