3

I see a lot of examples that use Files.walkFileTree() to copy a directory and its contents from one location to another, but they fail to take the directory's file attributes and permissions into consideration. Meaning, they just invoke Files.createDirectories() without any attributes or permissions.

How does one copy a directory (and its contents) from one location to another without losing file attributes or permissions, using the Java7 core classes?

Community
  • 1
  • 1
Gili
  • 86,244
  • 97
  • 390
  • 689
  • And another: http://stackoverflow.com/a/20085222/281545 and the vanilla one: http://docs.oracle.com/javase/tutorial/essential/io/examples/Copy.java – Mr_and_Mrs_D Apr 17 '14 at 16:17
  • @Mr_and_Mrs_D, the examples you quoted do not copy file permissions (only attributes). They also do not copy directory permissions and attributes. – Gili Apr 17 '14 at 21:01

1 Answers1

13

Answering my own question:

/**
 * Copies a directory.
 * <p>
 * NOTE: This method is not thread-safe.
 * <p>
 * 
 * @param source
 *            the directory to copy from
 * @param target
 *            the directory to copy into
 * @throws IOException
 *             if an I/O error occurs
 */
private static void copyDirectory(final Path source, final Path target)
        throws IOException {
    Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS),
        Integer.MAX_VALUE, new FileVisitor<Path>() {

            @Override
            public FileVisitResult preVisitDirectory(Path dir,
                    BasicFileAttributes sourceBasic) throws IOException {
                Path targetDir = Files.createDirectories(target
                    .resolve(source.relativize(dir)));
                AclFileAttributeView acl = Files.getFileAttributeView(dir,
                    AclFileAttributeView.class);
                if (acl != null)
                    Files.getFileAttributeView(targetDir,
                        AclFileAttributeView.class).setAcl(acl.getAcl());
                DosFileAttributeView dosAttrs = Files.getFileAttributeView(
                    dir, DosFileAttributeView.class);
                if (dosAttrs != null) {
                    DosFileAttributes sourceDosAttrs = dosAttrs
                        .readAttributes();
                    DosFileAttributeView targetDosAttrs = Files
                        .getFileAttributeView(targetDir,
                            DosFileAttributeView.class);
                    targetDosAttrs.setArchive(sourceDosAttrs.isArchive());
                    targetDosAttrs.setHidden(sourceDosAttrs.isHidden());
                    targetDosAttrs.setReadOnly(sourceDosAttrs.isReadOnly());
                    targetDosAttrs.setSystem(sourceDosAttrs.isSystem());
                }
                FileOwnerAttributeView ownerAttrs = Files
                    .getFileAttributeView(dir, FileOwnerAttributeView.class);
                if (ownerAttrs != null) {
                    FileOwnerAttributeView targetOwner = Files
                        .getFileAttributeView(targetDir,
                            FileOwnerAttributeView.class);
                    targetOwner.setOwner(ownerAttrs.getOwner());
                }
                PosixFileAttributeView posixAttrs = Files
                    .getFileAttributeView(dir, PosixFileAttributeView.class);
                if (posixAttrs != null) {
                    PosixFileAttributes sourcePosix = posixAttrs
                        .readAttributes();
                    PosixFileAttributeView targetPosix = Files
                        .getFileAttributeView(targetDir,
                            PosixFileAttributeView.class);
                    targetPosix.setPermissions(sourcePosix.permissions());
                    targetPosix.setGroup(sourcePosix.group());
                }
                UserDefinedFileAttributeView userAttrs = Files
                    .getFileAttributeView(dir,
                        UserDefinedFileAttributeView.class);
                if (userAttrs != null) {
                    UserDefinedFileAttributeView targetUser = Files
                        .getFileAttributeView(targetDir,
                            UserDefinedFileAttributeView.class);
                    for (String key : userAttrs.list()) {
                        ByteBuffer buffer = ByteBuffer.allocate(userAttrs
                            .size(key));
                        userAttrs.read(key, buffer);
                        buffer.flip();
                        targetUser.write(key, buffer);
                    }
                }
                // Must be done last, otherwise last-modified time may be
                // wrong
                BasicFileAttributeView targetBasic = Files
                    .getFileAttributeView(targetDir,
                        BasicFileAttributeView.class);
                targetBasic.setTimes(sourceBasic.lastModifiedTime(),
                    sourceBasic.lastAccessTime(),
                    sourceBasic.creationTime());
                return FileVisitResult.CONTINUE;
            }

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

            @Override
            public FileVisitResult
                    visitFileFailed(Path file, IOException e)
                            throws IOException {
                throw e;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir,
                    IOException e) throws IOException {
                if (e != null) throw e;
                return FileVisitResult.CONTINUE;
            }
        });
}

This code hasn't been tested extensively, but it works for me.

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
Gili
  • 86,244
  • 97
  • 390
  • 689
  • Two questions : 1. why not the vanilla one: http://docs.oracle.com/javase/tutorial/essential/io/examples/Copy.java 2. why not an implementation of SimpleFileVisitor – Mr_and_Mrs_D Apr 17 '14 at 16:14
  • The design could also profit from an enum which would accept a Class in its constructor – Mr_and_Mrs_D Apr 17 '14 at 16:29
  • @Mr_and_Mrs_D, the "vanilla one" copies attributes but not permissions. Which line(s) do you believe do so? – Gili Apr 17 '14 at 20:55
  • @Mr_and_Mrs_D, I don't understand what you mean about the enum and constructor. `Class` is not an `enum`, nor is it clear what constructor you are referring to. – Gili Apr 17 '14 at 20:58
  • Re: enum: https://www.dropbox.com/s/ngrwqnhzx7o7iqu/CopyDirectory.java. Re: the vanilla one - was just asking, I see now. Notice I changed the anonymous class to nested and made it implement SimpleFileVisitor (as visitFileFailed and postVisitDirectory) had the default behavior in your anonymous implementation – Mr_and_Mrs_D Apr 17 '14 at 21:01
  • @Mr_and_Mrs_D, that's just a way of splitting up the code into smaller chunks. For the purposes of Stackoverflow, I think what we have is good enough. – Gili Apr 17 '14 at 21:09
  • @Mr_and_Mrs_D What is this essential library stuff? Is that from Oracle? In what library can it be found? – mjs Jan 17 '15 at 10:13
  • @Gili Is it still true that permissions aren't copied? From the tests I tried, posix permissions are copied over. – loosebazooka Jun 06 '16 at 20:48
  • This is amazing! Still working on Java 9. Thankyou very much. I don't know about permissions but I only wanted to copy a simple directory anyway. –  Dec 06 '17 at 02:51