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,