【问题标题】:Java move sub directory with files and structure to parent directoryJava将带有文件和结构的子目录移动到父目录
【发布时间】:2019-03-21 16:32:56
【问题描述】:

试图将文件从子目录连同结构一起移动到父目录。我无法使用Files.move() 完成此操作。为了说明这个问题,请参见下面的目录结构。

$ tree
.
└── b
    ├── c
    │   ├── cfile.gtxgt
    │   └── d
    │       ├── dfile.txt
    │       └── e
    └── x
        └── y
            └── z
                ├── 2.txt
                └── p
                    ├── file1.txt
                    └── q
                        ├── file
                        ├── file2.txt
                        └── r
                            └── 123.txt

我想通过 Java 模拟下面的移动命令。

$ mv b/x/y/z/* b/c
b/x/y/z/2.txt -> b/c/2.txt
b/x/y/z/p -> b/c/p

并且输出应该类似于

$ tree
.
└── b
    ├── c
    │   ├── 2.txt
    │   ├── cfile.gtxgt
    │   ├── d
    │   │   ├── dfile.txt
    │   │   └── e
    │   └── p
    │       ├── file1.txt
    │       └── q
    │           ├── file
    │           ├── file2.txt
    │           └── r
    │               └── 123.txt
    └── x
        └── y
            └── z

在此移动中,z 目录下的所有文件和目录都已移动到c

我已经尝试过这样做:

public static void main(String[] args) throws IOException{
    String aPath = "/tmp/test/a/";
    String relativePathTomove = "b/x/y/z/";
    String relativePathToMoveTo = "b/c";

    Files.move(Paths.get(aPath, relativePathTomove), Paths.get(aPath, relativePathToMoveTo), StandardCopyOption.REPLACE_EXISTING);

}

但是,这会导致抛出java.nio.file.DirectoryNotEmptyException: /tmp/test/a/b/c 的异常,如果去掉REPLACE_EXISTING 选项,代码会抛出java.nio.file.FileAlreadyExistsException: /tmp/test/a/b/c

这个question 有一个使用递归函数来解决这个问题的答案。但就我而言,这将涉及进一步的复杂性,因为我什至需要在新位置重新创建子目录结构。

我没有尝试使用commons-io 实用方法org.apache.commons.io.FileUtils#moveDirectoryToDirectory 的选项,因为此代码似乎首先复制文件,然后从原始位置删除它们。在我的情况下,文件很大,因此这不是首选。

如何在不借助复制的情况下实现 java 中的移动功能。单个文件移动是我唯一的选择吗?

TLDR:如何在 java 中模拟 mv 功能,以将带有文件和结构的子目录移动到父目录。

【问题讨论】:

    标签: java nio


    【解决方案1】:

    我最终这样做了:

    像这样创建一个FileVisitor 实现:

    package com.test.files;
    
    import org.apache.log4j.Logger;
    
    import javax.validation.constraints.NotNull;
    import java.io.IOException;
    import java.nio.file.FileVisitResult;
    import java.nio.file.FileVisitor;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.attribute.BasicFileAttributes;
    import java.util.Objects;
    
    import static java.nio.file.FileVisitResult.TERMINATE;
    
    
    public class MoveFileVisitor implements FileVisitor<Path> {
    
        private static final Logger LOGGER = Logger.getLogger(MoveFileVisitor.class);
        private final Path target;
        private final Path source;
    
        public MoveFileVisitor(@NotNull Path source, @NotNull Path target) {
            this.target = Objects.requireNonNull(target);
            this.source = Objects.requireNonNull(source);
        }
    
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            Path relativePath = source.relativize(dir);
            Path finalPath = target.resolve(relativePath);
            Files.createDirectories(finalPath);
            return FileVisitResult.CONTINUE;
        }
    
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Path relativePath = source.relativize(file);
            Path finalLocation = target.resolve(relativePath);
            Files.move(file, finalLocation);
            return FileVisitResult.CONTINUE;
        }
    
        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) {
            LOGGER.error("Failed to visit file during move" + file.toAbsolutePath(), exc);
            return TERMINATE;
        }
    
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
    
    
    }
    

    然后像这样和这个访客一起走这条路:

        String source = "/temp/test/a/b/x/y/z";
        String target = "/temp/test/a/b/c";
    
        MoveFileVisitor visitor = new MoveFileVisitor(Paths.get(source), Paths.get(target));
        Files.walkFileTree(Paths.get(source), visitor);
    

    【讨论】:

    • 你不需要这些。一个带有正确参数的File.rename() 可以在一行代码中完成很多工作。
    • @user207421 - 我假设您的意思是File.renameTo() 方法。此方法的文档建议使用Files.move(),它会抛出上面列出的异常。另外我不认为renameTo 会考虑到目标目录有一些文件并且这里的文件需要合并到它们中。如果所有这些都是可能的,你能给我正确的参数吗?我不想为已经实现的东西编写代码。
    猜你喜欢
    • 2017-05-27
    • 2016-04-10
    • 1970-01-01
    • 2016-05-03
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 2020-12-30
    相关资源
    最近更新 更多