Java копирует файлы, соответствующие шаблону из многих папок в другую папку - PullRequest
0 голосов
/ 25 октября 2019

Пожалуйста, посмотрите на код, который у меня есть, и, если возможно, объясните, что я делаю неправильно. Я пытаюсь научиться.

Я создал небольшую программу для поиска типа файла в каталоге и во всех его подкаталогах и скопировал их в другую папку.

Код

import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class FandFandLoop {

    public static void main(String[] args) {

        final File folder = new File("C:/Users/ina/src");

        List<String> result = new ArrayList<>();

        search(".*\\.txt", folder, result);

        File to = new File("C:/Users/ina/dest");

        for (String s : result) {
            System.out.println(s);
            File from = new File(s);
            try {
                copyDir(from.toPath(), to.toPath());
                System.out.println("done");
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }
        }

    }

    public static void copyDir(Path src, Path dest) throws IOException {
        Files.walk(src)
                .forEach(source -> {
                    try {
                        Files.copy(source, dest.resolve(src.relativize(source)),
                                        StandardCopyOption.REPLACE_EXISTING);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
    }

    public static void search(final String pattern, final File folder, List<String> result) {
        for (final File f : folder.listFiles()) {

            if (f.isDirectory()) {
                search(pattern, f, result);
            }

            if (f.isFile()) {
                if (f.getName().matches(pattern)) {
                    result.add(f.getAbsolutePath());
                }
            }

        }
    }

}

Это работает, но на самом деле это взять мои .txt файлы и записать их в другой файл с именем dest без расширения.

И в какой-то момент он удаляет папку dest.

Удаление происходит из-за StandardCopyOption.REPLACE_EXISTING, если я понимаю, но то, что я хотел бы получить, было то, чтоесли несколько файлов имеют одно и то же имя, то должна храниться только одна его копия.

1 Ответ

1 голос
/ 25 октября 2019

Нет необходимости вызывать Files.walk для соответствующих исходных файлов.

Вы можете улучшить этот код, полностью переключившись на использование java.nio.file.Path и не смешивая пути строк и File объекты. Кроме того, вместо рекурсивного вызова File.listFiles() вы можете использовать Files.walk или даже лучше Files.find.

Так что вместо этого вы можете использовать следующее:

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.stream.Stream;

public class CopyFiles {
    public static void copyFiles(Path src, Path dest, PathMatcher matcher, CopyOption... copyOptions) throws IOException {
        // Argument validation
        if (!Files.isDirectory(src)) {
            throw new IllegalArgumentException("Source '" + src + "' is not a directory");
        }
        if (!Files.isDirectory(dest)) {
            throw new IllegalArgumentException("Destination '" + dest + "' is not a directory");
        }
        Objects.requireNonNull(matcher);
        Objects.requireNonNull(copyOptions);

        BiPredicate<Path, BasicFileAttributes> filter = (path, attributes) -> attributes.isRegularFile() && matcher.matches(path);

        // Use try-with-resources to close stream as soon as it is not longer needed
        try (Stream<Path> files = Files.find(src, Integer.MAX_VALUE, filter)) {
            files.forEach(file -> {
                Path destFile = dest.resolve(src.relativize(file));
                try {
                    copyFile(file, destFile, copyOptions);
                }
                // Stream methods do not allow checked exceptions, have to wrap it
                catch (IOException ioException) {
                    throw new UncheckedIOException(ioException);
                }
            });
        }
        // Wrap UncheckedIOException; cannot unwrap it to get actual IOException 
        // because then information about the location where the exception was wrapped 
        // will get lost, see Files.find doc
        catch (UncheckedIOException uncheckedIoException) {
            throw new IOException(uncheckedIoException);
        }
    }

    private static void copyFile(Path srcFile, Path destFile, CopyOption... copyOptions) throws IOException {
        Path destParent = destFile.getParent();

        // Parent might be null if dest is empty path
        if (destParent != null) {
            // Create parent directories before copying file
            Files.createDirectories(destParent);
        }

        Files.copy(srcFile, destFile, copyOptions);
    }

    public static void main(String[] args) throws IOException {
        Path srcDir = Paths.get("path/to/src");
        Path destDir = Paths.get("path/to/dest");
        // Could also use FileSystem.getPathMatcher
        PathMatcher matcher = file -> file.getFileName().toString().endsWith(".txt");
        copyFiles(srcDir, destDir, matcher);
    }
}

...