Преместване на не празна директория рекурсивно с помощта на Java NIO.2 FileVisitor и Files.walkFileTree()

Видях много примери за това как да копирате или изтривате файлове рекурсивно с помощта на Java NIO.2. Например, това е как да копирате папка с цялото й съдържание:

/**
 * Copies a folder with all contents recursively. Class implements
 * {@code FileVisitor} interface.
 * @author Ernestas Gruodis
 */
public static class TreeCopy implements FileVisitor<Path> {

        private final Path source;
        private final Path target;
        private final boolean replace;
        private final CopyOption[] options;
        private final ArrayList<Object[]> events = new ArrayList<>();

        /**
         * Copies a folder with all contents recursively.
         *
         * @param source source file path.
         * @param target target file path.
         * @param replace {@code true} if existing file should be replaced.
         */
        public TreeCopy(Path source, Path target, boolean replace) {
            this.source = source;
            this.target = target;
            this.replace = replace;

            options = replace ? new CopyOption[]{COPY_ATTRIBUTES, REPLACE_EXISTING} : new CopyOption[0];
        }

        @Override
        public synchronized FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {

            Path newDir = target.resolve(source.relativize(dir));
            try {
                Files.copy(dir, newDir, options);
            } catch (FileAlreadyExistsException ex) {
                if (!replace) {
                    events.add(new Object[]{"Folder already exists", newDir, ex});
                    return FileVisitResult.TERMINATE;
                } else {
                    return FileVisitResult.CONTINUE;
                }
            } catch (DirectoryNotEmptyException ex) {
                //Ignore
            } catch (IOException ex) {
                events.add(new Object[]{"Unable to create a folder", newDir, ex});
                return FileVisitResult.SKIP_SUBTREE;
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public synchronized FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {

            Path newFile = target.resolve(source.relativize(file));
            try {
                Files.copy(file, newFile, options);
            } catch (FileAlreadyExistsException ex) {
                events.add(new Object[]{"File already exists", newFile, ex});
            } catch (NoSuchFileException ex) {
                events.add(new Object[]{"No such file", newFile.getParent(), ex});
            } catch (IOException ex) {
                events.add(new Object[]{"Unable to create a file", newFile, ex});
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public synchronized FileVisitResult postVisitDirectory(Path dir, IOException exc) {

            if (exc == null) {
                Path newDir = target.resolve(source.relativize(dir));
                try {
                    FileTime time = Files.getLastModifiedTime(dir);
                    Files.setLastModifiedTime(newDir, time);
                } catch (IOException ex) {
                    events.add(new Object[]{"Unable to copy all attributes to", newDir, ex});
                }
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public synchronized FileVisitResult visitFileFailed(Path file, IOException ex) {

            if (ex instanceof FileSystemLoopException) {
                events.add(new Object[]{"Cycle detected", file, ex});
            } else {
                events.add(new Object[]{"Unable to copy", file, ex});
            }
            return FileVisitResult.CONTINUE;
        }

        /**
         * Returns errors which happened while copying a directory.
         *
         * @return {@code ArrayList<Object[]>} error list, where at each entry
         * of {@code Object[]} index:
         * <ul><li> 0 - {@code String} - error description;
         * </li><li> 1 - {@code Path} - target folder/file path;
         * </li><li> 2 - {@code Exception} - specific exception.
         * </li></ul>
         */
        public ArrayList<Object[]> getEvents() {

            return events;
        }
    }


Path source = Paths.get("/toCopyDir"),
    target = Paths.get("/someDir2/etc/toCopyDir");

EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
TreeCopy tc = new TreeCopy(source, target, true);
try {
   Files.walkFileTree(source, opts, Integer.MAX_VALUE, tc);
} catch (IOException ex) {
   //Handle exception
}

Но как да преместите папката, съдържаща файлове? Има метод Files.move(Path source, Path target, CopyOption... options) throws IOException. Може ли някой да даде реален ефективен пример?

Мисля, че решението може да бъде да се използва Files.copy(...) в preVisitDirectory(...), а след това Files.delete(...) в postVisitDirectory(...), нещо подобно..


person Ernestas Gruodis    schedule 25.01.2016    source източник
comment
Files.move(нов файл(/път/към/srcFolder).toPath(), нов файл(/път/към/dstFolder).toPath(), StandardCopyOption.ATOMIC_MOVE); работи за мен   -  person guleryuz    schedule 25.01.2016
comment
И дори ако директорията не е празна?   -  person Ernestas Gruodis    schedule 25.01.2016
comment
Мисля, че с помощта на FileVisitor можете да постигнете повече контрол. Направих го с помощта на Files.copy(...) в preVisitDirectory(...) и след това Files.delete(...) в postVisitDirectory(...). Но все още не знам дали е възможно да използвам Files.move(...) заедно с FileVisitor, вероятно не.. само за един файл и празни папки.   -  person Ernestas Gruodis    schedule 25.01.2016
comment
Добре. windows 7 + jre 8 -› Files.move() работи дори директорията да не е празна. на linux + jre 8 хвърля изключение, когато dir не е празен. също в Windows Files.move() работи само когато изходната и целевата папки се намират в едно и също Filestore (дял, диск, том...), в противен случай хвърля изключение...   -  person guleryuz    schedule 26.01.2016
comment
@guleryuz - много полезна информация, благодаря. Java е междуплатформена, така че мисля, че за да се избегнат грешки, Files.walkFileTree(...) плюс FileVisitor трябва да се използва за преместване на не празни папки.   -  person Ernestas Gruodis    schedule 26.01.2016


Отговори (1)


Ето едно решение:

def moveDir(path: Path, to: Path): Unit = {
Files.createDirectories(to)
Files.walkFileTree(
  path,
  new SimpleFileVisitor[Path] {
    override def preVisitDirectory(
        dir: Path,
        attrs: BasicFileAttributes): FileVisitResult = {
      val targetDir = to.resolve(path.relativize(dir))
      try Files.createDirectory(targetDir)
      catch {
        case e: FileAlreadyExistsException =>
          if (!Files.isDirectory(targetDir)) throw e
      }
      FileVisitResult.CONTINUE
    }

    override def visitFile(file: Path,
                           attrs: BasicFileAttributes): FileVisitResult = {
      Files.move(file, to.resolve(path.relativize(file)))
      FileVisitResult.CONTINUE
    }
  }
)

}

person stackoverflowed    schedule 09.02.2019