Java 8: Copy directory recursively?
Asked Answered
B

7

44

I see that Java 8 has significantly cleaned up reading the contents of a file into a String:

String contents = new String(Files.readAllBytes(Paths.get(new URI(someUrl))));

I am wondering if there is something similar (cleaner/less code/more concise) for copying directories recursively. In Java 7 land, it's still something like:

public void copyFolder(File src, File dest) throws IOException{
    if(src.isDirectory()){
        if(!dest.exists()){
            dest.mkdir();
        }

        String files[] = src.list();

        for (String file : files) {
            File srcFile = new File(src, file);
            File destFile = new File(dest, file);

            copyFolder(srcFile,destFile);
        }

    } else {
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(dest); 

        byte[] buffer = new byte[1024];

        int length;
        while ((length = in.read(buffer)) > 0){
            out.write(buffer, 0, length);
        }

        in.close();
        out.close();
    }
}

Any improvements here in Java 8?

Beautifully answered 16/3, 2015 at 12:8 Comment(4)
FYI Files.readAllBytes(Paths.get(new URI(someUrl)) is available since Java 7.Irretrievable
else block can be done with java.nio.file.Files#copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) also available since 1.7Intangible
Check the example in the javadoc of FileVisitor: docs.oracle.com/javase/7/docs/api/java/nio/file/…Irretrievable
What about this org.apache.commons.io.FileUtils.copyDirectory(File, File) ... it does the job safety ... :DAlikee
S
33

In this way the code looks a bit simpler

import static java.nio.file.StandardCopyOption.*;

public  void copyFolder(Path src, Path dest) throws IOException {
    try (Stream<Path> stream = Files.walk(src)) {
        stream.forEach(source -> copy(source, dest.resolve(src.relativize(source))));
    }
}

private void copy(Path source, Path dest) {
    try {
        Files.copy(source, dest, REPLACE_EXISTING);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}
Stephanus answered 18/5, 2018 at 19:27 Comment(8)
This looks like it won't handle sub foldersInspirational
It does copy sub folders and their contents.Thunderpeal
Recursive does work here though, needs following line before walk to ensure destination parent directories exists: Files.createDirectories(dest.getParent());Hysteric
–1: stream is never closed. stackoverflow.com/a/60621544 is better.Flatfooted
What stream are you talking about? The solution does not use streams.Stephanus
docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/…Flatfooted
I removed the −1 (but I still prefer this answer, for the reasons stated there).Flatfooted
With the added try my code looses some of its appeal :-) So I think the main difference is if you pack more than one line into a closure. I think it is just a personal style preference of me.Stephanus
O
29

Using Files.walkFileTree:

  • you don't need to worry about closing Streams.
    (some other answers here forget that while using Files.walk)
  • handles IOException elegantly.
    (Some other answers here would become more difficult when adding proper exception handling instead of a simple printStackTrace)
    public void copyFolder(Path source, Path target, CopyOption... options)
            throws IOException {
        Files.walkFileTree(source, new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                    throws IOException {
                Files.createDirectories(target.resolve(source.relativize(dir).toString()));
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                    throws IOException {
                Files.copy(file, target.resolve(source.relativize(file).toString()), options);
                return FileVisitResult.CONTINUE;
            }
        });
    }

What this does is

  • walk recursively over all files in the directory.
  • When a directory is encountered (preVisitDirectory):
    create the corresponding one in the target directory.
  • When a regular file is encountered (visitFile):
    copy it.

options can be used to tailor the copy to your needs. For example to overwrite existing files in the target directory, use copyFolder(source, target, StandardCopyOption.REPLACE_EXISTING);

Outline answered 10/3, 2020 at 16:7 Comment(3)
Much better than many other answers, for the reasons stated. One drawback that should be mentioned: when a security manager prevents access to some file, the copy of the files which cannot be accessed will silently fail. (This should be rare however, usually no security manager is used.)Flatfooted
This solution is ok if there are no symbolic links, otherwise, it will copy they target dir and turn the link into an empty directory with the link name.Nonchalance
If you want to follow symbolic links, you can replace Files.walkFileTree(source, visitor) with Files.walkFileTree(source, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, visitor)Outline
V
16

How about the following code

public void copyFolder(File src, File dest) throws IOException {
        try (Stream<Path> stream = Files.walk(src.toPath())) {
            stream.forEachOrdered(sourcePath -> {

                try {
                    Files.copy(
                            /*Source Path*/
                            sourcePath,
                            /*Destination Path */
                            src.toPath().resolve(dest.toPath().relativize(sourcePath)));
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            });
        }
    }
Vitreous answered 13/12, 2015 at 17:35 Comment(4)
Determining the target path can more elegantly be dine using Path API : Path target = targetDir.resolve(sourceDir.relativize(path))Intangible
Hello Carl, How does order matters in copying files? I guess forEach should have been sufficient.Vitreous
Please do not catch Exception and printStackTrace. Wrapping the IOException into an UncheckedIOException would be appropriate.Flatfooted
@OlivierCailloux yes, that is more apt.Vitreous
X
10

This version uses Files.walk and Path parameters as Java 8 suggests.

public static void copyFolder(Path src, Path dest) {
    try {
        Files.walk( src ).forEach( s -> {
            try {
                Path d = dest.resolve( src.relativize(s) );
                if( Files.isDirectory( s ) ) {
                    if( !Files.exists( d ) )
                        Files.createDirectory( d );
                    return;
                }
                Files.copy( s, d );// use flag to override existing
            } catch( Exception e ) {
                e.printStackTrace();
            }
        });
    } catch( Exception ex ) {
        ex.printStackTrace();
    }
}
Xenos answered 4/9, 2017 at 4:34 Comment(0)
S
1

and one more version:

static void copyFolder(File src, File dest){
    // checks
    if(src==null || dest==null)
        return;
    if(!src.isDirectory())
        return;
    if(dest.exists()){
        if(!dest.isDirectory()){
            //System.out.println("destination not a folder " + dest);
            return;
        }
    } else {
        dest.mkdir();
    }

    if(src.listFiles()==null || src.listFiles().length==0)
        return;

    String strAbsPathSrc = src.getAbsolutePath();
    String strAbsPathDest = dest.getAbsolutePath();

    try {
        Files.walkFileTree(src.toPath(), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file,
                    BasicFileAttributes attrs) throws IOException {
                File dstFile = new File(strAbsPathDest + file.toAbsolutePath().toString().substring(strAbsPathSrc.length()));
                if(dstFile.exists())
                    return FileVisitResult.CONTINUE;

                if(!dstFile.getParentFile().exists())
                    dstFile.getParentFile().mkdirs();

                //System.out.println(file + " " + dstFile.getAbsolutePath());
                Files.copy(file, dstFile.toPath());

                return FileVisitResult.CONTINUE;
            }
        });

    } catch (IOException e) {
        //e.printStackTrace();
        return;
    }

    return;
}

its code use java8 Files.walkFileTree function.

Session answered 23/4, 2015 at 9:19 Comment(0)
S
0

my version:

static private void copyFolder(File src, File dest) {
    // checks
    if(src==null || dest==null)
        return;
    if(!src.isDirectory())
        return;
    if(dest.exists()){
        if(!dest.isDirectory()){
            //System.out.println("destination not a folder " + dest);
            return;
        }
    } else {
        dest.mkdir();
    }

    File[] files = src.listFiles();
    if(files==null || files.length==0)
        return;

    for(File file: files){
        File fileDest = new File(dest, file.getName());
        //System.out.println(fileDest.getAbsolutePath());
        if(file.isDirectory()){
            copyFolder(file, fileDest);
        }else{
            if(fileDest.exists())
                continue;

            try {
                Files.copy(file.toPath(), fileDest.toPath());
            } catch (IOException e) {
                //e.printStackTrace();
            }
        }
    }
}
Session answered 22/4, 2015 at 14:42 Comment(6)
Please consider adding some explanation about the code.Hypocycloid
I add some expensive checks. its usefull if no checks in external code, example - raw data from users.Session
@Session I just wanted to thank you for this code, most of the examples I have found do not work but this works perfectly, you have saved me a lot of time by automating this, thank you!!Einstein
Really? Are you going to call listFiles() on the same object multiple times???Pappus
Correct me if I'm mistaken @blake-mcbride but I don't see how he is calling listFiles() on the same object? It's outside of the for loop and is only called again for recursion in a lower directory...Schnitzel
Clearly, it's not being called multiple times now.... (My comment was made 2/16. It was modified two days later.....Pappus
M
-1

Can be used to copy source (file or directory) to target (directory)

void copy(Path source, Path target, boolean override) throws IOException {
    Path target = target.resolve(source.toFile().getName());
    Files.walkFileTree(source, new FileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            Path targetDir = target.resolve(source.relativize(dir));
            if(Files.notExists(targetDir)) {
                Files.createDirectory(targetDir);
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Files.copy(file, target.resolve(source.relativize(file))));
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
            throw new RuntimeException("Copying file " + file + " failed", exc);
            // Consider looking at FileVisitResult options... 
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            if (exc != null) {
                // TODO...
            }
            return FileVisitResult.CONTINUE; // Or whatever works for you
        }
    });
}
Marchant answered 5/8, 2021 at 16:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.