简体   繁体   中英

Java move sub directory with files and structure to parent directory

Trying to move files from a sub-directory along with the structure to a parent directory. And am not able to accomplish this using Files.move() . To illustrate the issue please see the below directory structure.

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

I want to emulate the below move command via 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

And the output should be something similar to

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

In this move all the files and directories under directory z have been moved to c .

I have tried to do this:

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);

}

However this causes this exception to the thrown java.nio.file.DirectoryNotEmptyException: /tmp/test/a/b/c and if the the REPLACE_EXISTING option is taken out the code throws a java.nio.file.FileAlreadyExistsException: /tmp/test/a/b/c .

This question has an answer that uses a recursive function to solve this problem. But in my case it will involve further complexity as I need to even re-created the sub-dir structure in the new location.

I have not tried the option of using the commons-io utility method org.apache.commons.io.FileUtils#moveDirectoryToDirectory as this code seems to be first copying files and then deleting them from the original location. And In my case the files are huge and hence this is not a preferred option.

How can I achieve the the move functionality in java without resorting to copying. Is individual file move my only option?

TLDR: How can I emulate the mv functionality in java for moving sub dir with files and structure to parent directory.

I ended up doing this:

Create a FileVisitor Implementation like so:

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;
    }


}

And then walking the path with this Visitor like so:

    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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM