关于nio:Java将具有文件和结构的子目录移动到父目录

Java move sub directory with files and structure to parent directory

尝试将文件从子目录以及结构移动到父目录。并且无法使用Files.move()完成此操作。为了说明问题,请参见下面的目录结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ 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模拟以下move命令。

1
2
3
$ 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

输出应该类似于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ 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

我试图做到这一点:

1
2
3
4
5
6
7
8
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

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

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

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

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


我最终这样做:

创建一个FileVisitor实现,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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;
    }


}

然后像这样与这位访问者一起走这条路:

1
2
3
4
5
    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);