How to copy a directory with its attributes/permissions from one location to another?
Asked Answered
A

1

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?

Apposite answered 14/7, 2013 at 17:16 Comment(2)
And another: https://mcmap.net/q/138890/-java-using-nio-files-copy-to-move-directory and the vanilla one: docs.oracle.com/javase/tutorial/essential/io/examples/Copy.javaSaxena
@Mr_and_Mrs_D, the examples you quoted do not copy file permissions (only attributes). They also do not copy directory permissions and attributes.Apposite
A
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.

Apposite answered 9/9, 2013 at 5:7 Comment(9)
Two questions : 1. why not the vanilla one: docs.oracle.com/javase/tutorial/essential/io/examples/Copy.java 2. why not an implementation of SimpleFileVisitorSaxena
The design could also profit from an enum which would accept a Class<T extends FileAttributeView (or AttributeView)> in its constructorSaxena
@Mr_and_Mrs_D, the "vanilla one" copies attributes but not permissions. Which line(s) do you believe do so?Apposite
@Mr_and_Mrs_D, I don't understand what you mean about the enum and constructor. Class<T extends FileAttributeView> is not an enum, nor is it clear what constructor you are referring to.Apposite
Re: enum: 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 implementationSaxena
@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.Apposite
@Saxena What is this essential library stuff? Is that from Oracle? In what library can it be found?Stinkpot
@Apposite Is it still true that permissions aren't copied? From the tests I tried, posix permissions are copied over.Chessa
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.Intervale

© 2022 - 2024 — McMap. All rights reserved.