1. 删除指定文件夹底下的文件夹及文件

1.1 使用FileUtils删除

  • 优点:使用工具类,代码干净,速度快
  • 缺点:无法屏蔽一些不想删除或者根据条件删除文件,无法得知删除数量

1.1.1 引入依赖

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>

1.1.2 使用

        File file = new File("/home/yhc/workSpace/log");        
        try {
            FileUtils.deleteDirectory(file);
        } catch (IOException e) {
            e.printStackTrace();
        }	

1.1.3 其他功能

具体查看这位大佬的文章 文件操作工具类:FileUtils

2.1 参数

参数名含义默认值
path删除的文件路径,支持多个路径,用分割
PATH_PATTERN切割路径含有/[或[的正则表达式Pattern.compile(“^((?![\\/]\[).)*”)
RULE_PATTERN切割路径含有[的正则表达式Pattern.compile(“\[.*”)
days保留天数,超过此天数的文件将被删除30
dayTimes一天的毫秒数24 * 60 * 60 * 1000
ignoreName忽略的文件名,多个文件名用;分割123.txt;123.log
pathRetention是否按路径删除文件true
下列为多线程特有参数
threadNum线程池大小2

path入参必须含有yyyy mm dd 不区分大小写,/不区分。类似[YYYY]/[YYYYMM]/[YYYYMMDD]、[YYYY]/[MM]/[DD]、[YYYYMMDD]、[YYYY]/[YYYYMM]/[DD]等皆支持

2.2 代码(单线程)

package com.api.apidemo.tool.file;

import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author yhc
 * @description 批量删除文件-普通方式
 */
public class BatchDeleteFile {
    /**
     * 删除的文件路径,支持多个路径,用|分割
     */
    public static final String path = "/home/cathay10/workSpace/log/[yyyyMMdd]";
    /**
     * 切割路径含有/[或\[的正则表达式
     */
    public static final Pattern PATH_PATTERN = Pattern.compile("^((?![\\\\/]\\[).)*");
    /**
     * 切割路径含有[的正则表达式
     */
    public static final Pattern RULE_PATTERN = Pattern.compile("\\[.*");
    /**
     * 保留天数,超过此天数的文件将被删除
     */
    public static final int days = 30;
    /**
     * 一天的毫秒数
     */
    public static final int dayTimes = 24 * 60 * 60 * 1000;
    /**
     * 忽略的文件名,多个文件名用;分割
     */
    public static final String ignoreName = "123.txt;123.log";
    /**
     * 是否按路径删除文件
     */
    public static final boolean pathRetention = true;

    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        long num = batchDeleteFile();
        // 打印总删除文件数
        long stop = System.currentTimeMillis();
        System.out.println("执行结束,耗时:" + (stop - begin) + "ms" + " 删除文件数量:" + num);
    }

    /**
     * 批量删除文件
     */
    public static long batchDeleteFile() {
        // 删除数量
        long num = 0;
        // 删除路径集合 有需要可以添加多个路径,按 | 分割
        List sourceList = Arrays.asList(path.split("\\|"));
        int length = sourceList.size();
        if (length > 0) {
            for (int m = 0; m < length; m++) {
                // 判断目录是否含有日期格式
                String targetPath = replaceDate(sourceList.get(m).toString());
                // 处理通配符,将路径中的通配符替换为正则表达式
                targetPath = replaceWildcard(targetPath);
                if (pathRetention) {
                    // 按日期格式路径删除文件
                    // 截取掉除了年月日之外的路径
                    String dirPath = pathSplit(sourceList.get(m).toString());
                    File dir = new File(dirPath);
                    if (dir.exists()) {
                        // 当前日期
                        LocalDate nowDate = LocalDate.now();
                        String rule = ruleSplit(sourceList.get(m).toString());
                        num = num + judgeDocumentPath(dir, ignoreName, nowDate, rule);
                    }
                } else {
                    String dirPath = pathSplit(targetPath);
                    // 按最后修改时间删除文件
                    File file = new File(dirPath);
                    if (file.exists()) {
                        num = num + judgeDocument(file, ignoreName);
                    }
                }
            }
        }
        return num;
    }

    /**
     * 处理文件路径,替换日期格式占位符为当前日期
     *
     * @param path 原始路径
     * @return 处理后的路径,如果没有找到日期占位符,则返回原路径
     */
    public static String replaceDate(String path) {
        String targetPath;
        if (StringUtils.containsIgnoreCase(path, "[yyyy]/[MM]/[dd]")) {
            targetPath = replaceIgnoreCase(path, "[yyyy]/[MM]/[dd]", LocalDate.now().toString().replace("-", "/"));
        } else if (StringUtils.containsIgnoreCase(path, "[yyyyMMdd]")) {
            targetPath = replaceIgnoreCase(path, "[yyyymmdd]", LocalDate.now().toString().replace("-", ""));
        } else {
            targetPath = path;
        }
        return targetPath;
    }

    /**
     * 处理文件路径,将路径中的通配符替换为正则表达式
     *
     * @param path 原始路径
     * @return 处理后的路径,如果没有找到通配符,则返回原路径
     */
    public static String replaceWildcard(String path) {
        path.replaceAll("\\*", ".*").replaceAll("\\?", ".");
        return path;
    }

    /**
     * 获取路径中是否包含日期格式
     *
     * @param targetPath 路径
     * @return 处理后的路径,截取第一次遇到日期格式前的路径,如果没有找到有效的日期格式,则返回原路径
     */
    public static String pathSplit(String targetPath) {
        Matcher pathMatcher = PATH_PATTERN.matcher(targetPath);
        while (pathMatcher.find()) {
            if (pathMatcher.group() != null) {
                return pathMatcher.group();
            }
        }
        // 没有找到有效的日期
        return null;
    }

    /**
     * 获取路径中的日期规则正则
     *
     * @param targetPath 路径
     * @return 规则正则
     */
    public static String ruleSplit(String targetPath) {
        Matcher ruleMatcher = RULE_PATTERN.matcher(targetPath);
        String ruleContent;
        while (ruleMatcher.find()) {
            ruleContent = replaceCharsInSquareBrackets(ruleMatcher.group());
            if (ruleContent != null) {
                return ruleContent;
            }
        }
        // 没有找到有效的日期
        return null;
    }

    /**
     * 替换方括号内的时间格式字符,分隔符[]
     *
     * @param str
     * @return 替换后的字符串
     */
    public static String replaceCharsInSquareBrackets(String str) {
        // 正则表达式匹配方括号内的内容
        String regex = "\\[(.*?)\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);

        // 存储原始和替换后的内容
        List<String> originalMatches = new ArrayList<>();
        List<String> replacedMatches = new ArrayList<>();

        // 提取匹配的内容并进行替换
        while (matcher.find()) {
            originalMatches.add(matcher.group());
            String match = matcher.group();
            String match2 = match.replace("YYYY", "yyyy").replace("mm", "MM").replace("DD", "dd");
            replacedMatches.add(match2);
        }

        // 将原始字符串分解,然后用替换后的内容替换匹配的子串
        StringBuilder replacedStr = new StringBuilder(str);
        for (int i = 0; i < originalMatches.size(); i++) {
            int startIndex = replacedStr.indexOf(originalMatches.get(i));
            int endIndex = startIndex + originalMatches.get(i).length();
            replacedStr.replace(startIndex, endIndex, replacedMatches.get(i));
        }
        return replacedStr.toString().replaceAll("\\[", "").replaceAll("\\]", "");
    }

    /**
     * 替换字符串,不区分大小写
     *
     * @param original 原始字符串
     * @param from     要替换的字符串
     * @param to       替换后的字符串
     * @return 替换后的字符串
     */
    public static String replaceIgnoreCase(String original, String from, String to) {
        // 将要查找的字符串转换为正则表达式形式,并添加不区分大小写的标志(?i)
        String regex = "(?i)" + Pattern.quote(from);

        // 使用正则表达式进行替换
        return original.replaceAll(regex, to);
    }

    /**
     * 文件处理
     *
     * @param file       要处理的文件夹
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long judgeDocumentPath(File file, String ignoreName, LocalDate nowDate, String rule) {
        long num = 0;
        File[] files = file.listFiles();
        for (File file1 : files) {
            if (file1.isDirectory()) {
                //调用方法循环完成对文件夹中文件的删除
                Pattern PATH_RELE_PATTERN = Pattern.compile(rule.replaceAll("[/\\\\]", "[/\\\\\\\\]").replace("yyyy", "(\\d{4})").replace("MM", "(\\d{2})").replace("dd", "(\\d{2})"));
                Matcher matcher = PATH_RELE_PATTERN.matcher(file1.getAbsolutePath());
                if (matcher.find()) {
                    LocalDate pathDate = dateConversion(matcher.group(), rule);
                    long betweenDays = ChronoUnit.DAYS.between(pathDate, nowDate);
                    if (betweenDays > days) {
                        num = num + delFilePath(file1, ignoreName);
                    }
                } else {
                    //调用方法循环完成对文件夹中文件的删除
                    num = num + judgeDocumentPath(file1, ignoreName, nowDate, rule);
                }
            }
        }
        return num;
    }

    /**
     * 循环删除目录下的文件
     *
     * @param file 要删除的文件夹
     * @return 处理的文件数量
     */
    public static long delFilePath(File file, String ignoreName) {
        long num = 0;
        if (file.list().length < 1) {
            deleteDirectoryPath(file);
        } else {
            File[] files = file.listFiles();
            for (File file1 : files) {
                if (file1.isDirectory()) {
                    num = num + delFilePath(file1, ignoreName);
                } else if (!getIsNotLike(ignoreName, file1.getName())) {
                    file1.delete();
                    ++num;
                }
            }
            deleteDirectoryPath(file);
        }
        return num;
    }

    /**
     * 删除文件夹
     *
     * @param files
     */
    public static void deleteDirectoryPath(File files) {
        if (files.exists() && files.list().length < 1) {
            try {
                Path path = files.toPath();
                Files.delete(path);
                deleteDirectoryPath(new File(path.getParent().toString()));
            } catch (IOException e) {
            }
        }
    }

    /**
     * 判断文件名中是否包含关键字,如果包含则剔除
     *
     * @param notLikeStr
     * @param fileName
     * @return
     */
    public static boolean getIsNotLike(String notLikeStr, String fileName) {
        String[] strArr = notLikeStr.split(";");
        for (int i = 0; i < strArr.length; i++) {
            if (fileName.contains(strArr[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    private static long judgeDocument(File file, String ignoreName) {
        long num = 0;
        File[] files = file.listFiles();
        for (File file1 : files) {
            if (file1.isDirectory()) {
                //调用方法循环完成对文件夹中文件的删除
                num = num + delFile(file1, ignoreName);
            } else {
                num = num + analysisFiles(file1, ignoreName);
            }
        }
        return num;
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long delFile(File file, String ignoreName) {
        long num = 0;
        if (file.list().length < 1) {
            deleteDirectory(file);
        } else {
            File[] files = file.listFiles();
            for (File file1 : files) {
                if (file1.isDirectory()) {
                    num = num + delFile(file1, ignoreName);
                } else {
                    num = num + analysisFiles(file1, ignoreName);
                }
                deleteDirectory(file);
            }
        }
        return num;
    }

    /**
     * 删除文件夹
     *
     * @param files 要删除的文件夹
     */
    public static void deleteDirectory(File files) {
        if (files.list().length < 1) {
            try {
                Path path = files.toPath();
                Files.delete(path);
            } catch (IOException e) {
            }
        }
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long analysisFiles(File file, String ignoreName) {

        long num = 0;
        //系统当前时间
        double nowTime = System.currentTimeMillis();
        Path path = file.toPath();
        //根据创建时间和系统当前时间判断文件存在时间是否大于要求天数(CLEANTIMEs * 24 * 60 * 60 * 1000)
        try {
            BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
            BasicFileAttributes basicAttribs = basicView.readAttributes();
            String fileName = file.getName();
            if (getIsNotLike(ignoreName, fileName)) {
                return 0;
            }
            //最后修改时间
            Date savaTime = new Date(basicAttribs.lastModifiedTime().toMillis());
            double saveTimes = savaTime.getTime();
            if (nowTime - saveTimes > (double) days * dayTimes) {
                Files.delete(path);
                ++num;
            }
        } catch (IOException e1) {
        }
        return num;
    }

    /**
     * 转换日期格式,支持格式为:formats
     *
     * @param path 路径
     * @return LocalDate 转换后的日期
     */
    public static LocalDate dateConversion(String path, String rule) {
        String[] formats = {rule.replace("\\", "/"), rule.replace("/", "\\")};
        for (String format : formats) {
            try {
                LocalDate localDate = LocalDate.parse(path, DateTimeFormatter.ofPattern(format));
                if (localDate != null) {
                    return localDate;
                }
            } catch (DateTimeParseException e) {
                // 没有找到有效的日期
            }
        }
        return null;
    }
}

2.3 代码(线程池)

package com.api.apidemo.tool.file;

import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author yhc
 * @description: 批量删除文件-多线程方式
 */
public class BatchDeleteFileThread {
    /**
     * 删除的文件路径,支持多个路径,用|分割
     */
    public static final String path = "/home/cathay10/workSpace/log/[yyyy]/[MM]/[dd]";
    /**
     * 切割路径含有/[或\[的正则表达式
     */
    public static final Pattern PATH_PATTERN = Pattern.compile("^((?![\\\\/]\\[).)*");
    /**
     * 切割路径含有[的正则表达式
     */
    public static final Pattern RULE_PATTERN = Pattern.compile("\\[.*");
    /**
     * 保留天数,超过此天数的文件将被删除
     */
    public static final int days = 30;
    /**
     * 一天的毫秒数
     */
    public static final int dayTimes = 24 * 60 * 60 * 1000;
    /**
     * 线程池大小
     */
    public static final int threadNum = 2;
    /**
     * 忽略的文件名,多个文件名用;分割
     */
    public static final String ignoreName = "123.txt;123.log";
    /**
     * 是否按路径删除文件
     */
    public static final boolean pathRetention = true;

    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        long num = batchDeleteFile();

        // 打印总删除文件数
        long stop = System.currentTimeMillis();
        System.out.println("执行结束,耗时:" + (stop - begin) + "ms" + " 删除文件数量:" + num);
    }

    /**
     * 批量删除文件
     */
    public static long batchDeleteFile() {
        // 删除数量
        long num = 0;
        // 删除路径集合 有需要可以添加多个路径,按 | 分割
        List sourceList = Arrays.asList(path.split("\\|"));
        int length = sourceList.size();
        if (length > 0) {
            for (int m = 0; m < length; m++) {
                // 判断目录是否含有日期格式
                String targetPath = replaceDate(sourceList.get(m).toString());
                // 处理通配符,将路径中的通配符替换为正则表达式
                targetPath = replaceWildcard(targetPath);
                if (pathRetention) {
                    // 按日期格式路径删除文件
                    // 截取掉除了年月日之外的路径
                    String dirPath = pathSplit(sourceList.get(m).toString());
                    File dir = new File(dirPath);
                    if (dir.exists()) {
                        // 当前日期
                        LocalDate nowDate = LocalDate.now();
                        String rule = ruleSplit(sourceList.get(m).toString());
                        num = num + judgeDocumentPath(dir, ignoreName, nowDate, rule);
                    }
                } else {
                    String dirPath = pathSplit(targetPath);
                    // 按最后修改时间删除文件
                    File file = new File(dirPath);
                    if (file.exists()) {
                        num = num + judgeDocument(file, ignoreName);
                    }
                }
            }
        }
        return num;
    }

    /**
     * 处理文件路径,替换日期格式占位符为当前日期
     *
     * @param path 原始路径
     * @return 处理后的路径,如果没有找到日期占位符,则返回原路径
     */
    public static String replaceDate(String path) {
        String targetPath;
        if (StringUtils.containsIgnoreCase(path, "[yyyy]/[MM]/[dd]")) {
            targetPath = replaceIgnoreCase(path, "[yyyy]/[MM]/[dd]", LocalDate.now().toString().replace("-", "/"));
        } else if (StringUtils.containsIgnoreCase(path, "[yyyyMMdd]")) {
            targetPath = replaceIgnoreCase(path, "[yyyymmdd]", LocalDate.now().toString().replace("-", ""));
        } else {
            targetPath = path;
        }
        return targetPath;
    }

    /**
     * 处理文件路径,将路径中的通配符替换为正则表达式
     *
     * @param path 原始路径
     * @return 处理后的路径,如果没有找到通配符,则返回原路径
     */
    public static String replaceWildcard(String path) {
        path.replaceAll("\\*", ".*").replaceAll("\\?", ".");
        return path;
    }

    /**
     * 获取路径中是否包含日期格式,支持格式为:formats
     *
     * @param targetPath 路径
     * @return 处理后的路径,截取第一次遇到日期格式前的路径,如果没有找到有效的日期格式,则返回原路径
     */
    public static String pathSplit(String targetPath) {
        Matcher pathMatcher = PATH_PATTERN.matcher(targetPath);
        while (pathMatcher.find()) {
            if (pathMatcher.group() != null) {
                return pathMatcher.group();
            }
        }
        // 没有找到有效的日期
        return null;
    }

    /**
     * 获取路径中的日期规则正则
     *
     * @param targetPath 路径
     * @return 规则正则
     */
    public static String ruleSplit(String targetPath) {
        Matcher ruleMatcher = RULE_PATTERN.matcher(targetPath);
        String ruleContent;
        while (ruleMatcher.find()) {
            ruleContent = replaceCharsInSquareBrackets(ruleMatcher.group());
            if (ruleContent != null) {
                return ruleContent;
            }
        }
        // 没有找到有效的日期
        return null;
    }

    /**
     * 替换方括号内的时间格式字符,分隔符[]
     *
     * @param str
     * @return 替换后的字符串
     */
    public static String replaceCharsInSquareBrackets(String str) {
        // 正则表达式匹配方括号内的内容
        String regex = "\\[(.*?)\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);

        // 存储原始和替换后的内容
        List<String> originalMatches = new ArrayList<>();
        List<String> replacedMatches = new ArrayList<>();

        // 提取匹配的内容并进行替换
        while (matcher.find()) {
            originalMatches.add(matcher.group());
            String match = matcher.group();
            String match2 = match.replace("YYYY", "yyyy").replace("mm", "MM").replace("DD", "dd");
            replacedMatches.add(match2);
        }

        // 将原始字符串分解,然后用替换后的内容替换匹配的子串
        StringBuilder replacedStr = new StringBuilder(str);
        for (int i = 0; i < originalMatches.size(); i++) {
            int startIndex = replacedStr.indexOf(originalMatches.get(i));
            int endIndex = startIndex + originalMatches.get(i).length();
            replacedStr.replace(startIndex, endIndex, replacedMatches.get(i));
        }
        return replacedStr.toString().replaceAll("\\[", "").replaceAll("\\]", "");
    }

    /**
     * 替换字符串,不区分大小写
     *
     * @param original 原始字符串
     * @param from     要替换的字符串
     * @param to       替换后的字符串
     * @return 替换后的字符串
     */
    public static String replaceIgnoreCase(String original, String from, String to) {
        // 将要查找的字符串转换为正则表达式形式,并添加不区分大小写的标志(?i)
        String regex = "(?i)" + Pattern.quote(from);

        // 使用正则表达式进行替换
        return original.replaceAll(regex, to);
    }

    /**
     * 文件处理
     *
     * @param file       要处理的文件夹
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long judgeDocumentPath(File file, String ignoreName, LocalDate nowDate, String rule) {
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        long num = 0;
        File[] files = file.listFiles();
        for (File file1 : files) {
            Future<Long> future = executorService.submit(new DeleteFileCallable(pathRetention, file1, ignoreName, nowDate, rule));
            try {
                num = num + future.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
        // 关闭线程池
        executorService.shutdown();
        return num;
    }

    /**
     * 文件处理
     *
     * @param file       要处理的文件夹
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long threadJudgeDocumentPath(File file, String ignoreName, LocalDate nowDate, String rule) {
        long num = 0;
        File[] files = file.listFiles();

        if (file.isDirectory()) {
            //调用方法循环完成对文件夹中文件的删除
            Pattern PATH_RELE_PATTERN = Pattern.compile(rule.replaceAll("[/\\\\]", "[/\\\\\\\\]").replace("yyyy", "(\\d{4})").replace("MM", "(\\d{2})").replace("dd", "(\\d{2})"));
            Matcher matcher = PATH_RELE_PATTERN.matcher(file.getAbsolutePath());
            if (matcher.find()) {
                LocalDate pathDate = dateConversion(matcher.group(), rule);
                long betweenDays = ChronoUnit.DAYS.between(pathDate, nowDate);
                if (betweenDays > days) {
                    num = num + delFilePath(file, ignoreName);
                }
            } else {
                for (File file1 : files) {
                    //调用方法循环完成对文件夹中文件的删除
                    num = num + threadJudgeDocumentPath(file1, ignoreName, nowDate, rule);
                }
            }
        }
        return num;
    }

    /**
     * 循环删除目录下的文件
     *
     * @param file 要删除的文件夹
     * @return 处理的文件数量
     */
    public static long delFilePath(File file, String ignoreName) {
        long num = 0;
        if (file.list().length < 1) {
            deleteDirectoryPath(file);
        } else {
            File[] files = file.listFiles();
            for (File file1 : files) {
                if (file1.isDirectory()) {
                    num = num + delFilePath(file1, ignoreName);
                } else if (!getIsNotLike(ignoreName, file1.getName())) {
                    file1.delete();
                    ++num;
                }
            }
            deleteDirectoryPath(file);
        }
        return num;
    }

    /**
     * 删除文件夹
     *
     * @param files
     */
    public static void deleteDirectoryPath(File files) {
        if (files.exists() && files.list().length < 1) {
            try {
                Path path = files.toPath();
                Files.delete(path);
                deleteDirectoryPath(new File(path.getParent().toString()));
            } catch (IOException e) {
            }
        }
    }

    /**
     * 判断文件名中是否包含关键字,如果包含则剔除
     *
     * @param notLikeStr
     * @param fileName
     * @return
     */
    public static boolean getIsNotLike(String notLikeStr, String fileName) {
        String[] strArr = notLikeStr.split(";");
        for (int i = 0; i < strArr.length; i++) {
            if (fileName.contains(strArr[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    private static long judgeDocument(File file, String ignoreName) {
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        long num = 0;
        File[] files = file.listFiles();
        for (File file1 : files) {
            Future<Long> future = executorService.submit(new DeleteFileCallable(pathRetention, file1, ignoreName, null, null));
            try {
                num = num + future.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
        // 关闭线程池
        executorService.shutdown();
        return num;
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    private static long threadJudgeDocument(File file, String ignoreName) {
        long num = 0;
        if (file.isDirectory()) {
            //调用方法循环完成对文件夹中文件的删除
            num = num + delFile(file, ignoreName);
        } else {
            num = num + analysisFiles(file, ignoreName);
        }
        return num;
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long delFile(File file, String ignoreName) {
        long num = 0;
        if (file.list().length < 1) {
            deleteDirectory(file);
        } else {
            File[] files = file.listFiles();
            for (File file1 : files) {
                if (file1.isDirectory()) {
                    num = num + delFile(file1, ignoreName);
                } else {
                    num = num + analysisFiles(file1, ignoreName);
                }
                deleteDirectory(file);
            }
        }
        return num;
    }

    /**
     * 删除文件夹
     *
     * @param files 要删除的文件夹
     */
    public static void deleteDirectory(File files) {
        if (files.list().length < 1) {
            try {
                Path path = files.toPath();
                Files.delete(path);
            } catch (IOException e) {
            }
        }
    }

    /**
     * 判断文件是否过期,如果过期则删除
     *
     * @param file       要处理的文件
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long analysisFiles(File file, String ignoreName) {

        long num = 0;
        //系统当前时间
        double nowTime = System.currentTimeMillis();
        Path path = file.toPath();
        //根据创建时间和系统当前时间判断文件存在时间是否大于要求天数(CLEANTIMEs * 24 * 60 * 60 * 1000)
        try {
            BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
            BasicFileAttributes basicAttribs = basicView.readAttributes();
            String fileName = file.getName();
            if (getIsNotLike(ignoreName, fileName)) {
                return 0;
            }
            //最后修改时间
            Date savaTime = new Date(basicAttribs.lastModifiedTime().toMillis());
            double saveTimes = savaTime.getTime();
            if (nowTime - saveTimes > (double) days * dayTimes) {
                Files.delete(path);
                ++num;
            }
        } catch (IOException e1) {
        }
        return num;
    }

    /**
     * 转换日期格式,支持格式为:formats
     *
     * @param path 路径
     * @return LocalDate 转换后的日期
     */
    public static LocalDate dateConversion(String path, String rule) {
        String[] formats = {rule.replace("\\", "/"), rule.replace("/", "\\")};
        for (String format : formats) {
            try {
                LocalDate localDate = LocalDate.parse(path, DateTimeFormatter.ofPattern(format));
                if (localDate != null) {
                    return localDate;
                }
            } catch (DateTimeParseException e) {
                // 没有找到有效的日期
            }
        }
        return null;
    }

    static class DeleteFileCallable implements Callable<Long> {
        private boolean pathRetention;
        private File file;
        private String ignoreName;
        private LocalDate nowDate;
        private String rule;

        public DeleteFileCallable(boolean pathRetention, File file, String ignoreName, LocalDate nowDate, String rule) {
            this.pathRetention = pathRetention;
            this.file = file;
            this.ignoreName = ignoreName;
            this.nowDate = nowDate;
            this.rule = rule;
        }

        @Override
        public Long call() {
            long num;
            Thread currentThread = Thread.currentThread();
            System.out.println("Thread ID: " + currentThread.getId() + " 文件路径:" + file.getAbsolutePath());
            if (pathRetention) {
                System.out.println("按日期格式路径删除文件");
                num = threadJudgeDocumentPath(file, ignoreName, nowDate, rule);
            } else {
                System.out.println("按文件最后修改时间删除文件");
                num = threadJudgeDocument(file, ignoreName);
            }
            return num;
        }
    }
}

2.4 代码(流)

package com.api.apidemo.tool.file;

import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * @author yhc
 * @description 批量删除文件-流方式
 */
public class BatchDeleteFileStream {
    /**
     * 删除的文件路径,支持多个路径,用|分割
     */
    public static final String path = "/home/cathay10/workSpace/log/[yyyy]/[MM]/[dd]";
    /**
     * 切割路径含有/[或\[的正则表达式
     */
    public static final Pattern PATH_PATTERN = Pattern.compile("^((?![\\\\/]\\[).)*");
    /**
     * 切割路径含有[的正则表达式
     */
    public static final Pattern RULE_PATTERN = Pattern.compile("\\[.*");
    /**
     * 保留天数,超过此天数的文件将被删除
     */
    public static final int days = 30;
    /**
     * 一天的毫秒数
     */
//    public static final int dayTimes = 24 * 60 * 60 * 1000;
    public static final int dayTimes = 1000;
    /**
     * 忽略的文件名,多个文件名用;分割
     */
    public static final String ignoreName = "123.txt;123.log";
    /**
     * 是否按路径删除文件
     */
    public static final boolean pathRetention = true;

    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        long num = batchDeleteFile();
        // 打印总删除文件数
        long stop = System.currentTimeMillis();
        System.out.println("执行结束,耗时:" + (stop - begin) + "ms" + " 删除文件数量:" + num);
    }

    /**
     * 批量删除文件
     */
    public static long batchDeleteFile() {
        // 删除数量
        long num = 0;
        // 删除路径集合 有需要可以添加多个路径,按 | 分割
        List sourceList = Arrays.asList(path.split("\\|"));
        int length = sourceList.size();
        if (length > 0) {
            for (int m = 0; m < length; m++) {
                // 判断目录是否含有日期格式
                String targetPath = replaceDate(sourceList.get(m).toString());
                // 处理通配符,将路径中的通配符替换为正则表达式
                targetPath = replaceWildcard(targetPath);
                if (pathRetention) {
                    // 按日期格式路径删除文件
                    // 截取掉除了年月日之外的路径
                    String dirPath = pathSplit(sourceList.get(m).toString());
                    File dir = new File(dirPath);
                    if (dir.exists()) {
                        // 当前日期
                        LocalDate nowDate = LocalDate.now();
                        String rule = ruleSplit(sourceList.get(m).toString());
                        num = num + judgeDocumentPath(dir, ignoreName, nowDate, rule);
                    }
                } else {
                    String dirPath = pathSplit(targetPath);
                    // 按最后修改时间删除文件
                    File file = new File(dirPath);
                    if (file.exists()) {
                        num = num + deleteFolder(file, ignoreName);
                    }
                }
            }
        }
        return num;
    }

    /**
     * 处理文件路径,替换日期格式占位符为当前日期
     *
     * @param path 原始路径
     * @return 处理后的路径,如果没有找到日期占位符,则返回原路径
     */
    public static String replaceDate(String path) {
        String targetPath;
        if (StringUtils.containsIgnoreCase(path, "[yyyy]/[MM]/[dd]")) {
            targetPath = replaceIgnoreCase(path, "[yyyy]/[MM]/[dd]", LocalDate.now().toString().replace("-", "/"));
        } else if (StringUtils.containsIgnoreCase(path, "[yyyyMMdd]")) {
            targetPath = replaceIgnoreCase(path, "[yyyymmdd]", LocalDate.now().toString().replace("-", ""));
        } else {
            targetPath = path;
        }
        return targetPath;
    }

    /**
     * 处理文件路径,将路径中的通配符替换为正则表达式
     *
     * @param path 原始路径
     * @return 处理后的路径,如果没有找到通配符,则返回原路径
     */
    public static String replaceWildcard(String path) {
        path.replaceAll("\\*", ".*").replaceAll("\\?", ".");
        return path;
    }

    /**
     * 获取路径中是否包含日期格式,支持格式为:formats
     *
     * @param targetPath 路径
     * @return 处理后的路径,截取第一次遇到日期格式前的路径,如果没有找到有效的日期格式,则返回原路径
     */
    public static String pathSplit(String targetPath) {
        Matcher pathMatcher = PATH_PATTERN.matcher(targetPath);
        while (pathMatcher.find()) {
            if (pathMatcher.group() != null) {
                return pathMatcher.group();
            }
        }
        // 没有找到有效的日期
        return null;
    }

    /**
     * 获取路径中的日期规则正则
     *
     * @param targetPath 路径
     * @return 规则正则
     */
    public static String ruleSplit(String targetPath) {
        Matcher ruleMatcher = RULE_PATTERN.matcher(targetPath);
        String ruleContent;
        while (ruleMatcher.find()) {
            ruleContent = replaceCharsInSquareBrackets(ruleMatcher.group());
            if (ruleContent != null) {
                return ruleContent;
            }
        }
        // 没有找到有效的日期
        return null;
    }

    /**
     * 替换方括号内的时间格式字符,分隔符[]
     *
     * @param str
     * @return 替换后的字符串
     */
    public static String replaceCharsInSquareBrackets(String str) {
        // 正则表达式匹配方括号内的内容
        String regex = "\\[(.*?)\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);

        // 存储原始和替换后的内容
        List<String> originalMatches = new ArrayList<>();
        List<String> replacedMatches = new ArrayList<>();

        // 提取匹配的内容并进行替换
        while (matcher.find()) {
            originalMatches.add(matcher.group());
            String match = matcher.group();
            String match2 = match.replace("YYYY", "yyyy").replace("mm", "MM").replace("DD", "dd");
            replacedMatches.add(match2);
        }

        // 将原始字符串分解,然后用替换后的内容替换匹配的子串
        StringBuilder replacedStr = new StringBuilder(str);
        for (int i = 0; i < originalMatches.size(); i++) {
            int startIndex = replacedStr.indexOf(originalMatches.get(i));
            int endIndex = startIndex + originalMatches.get(i).length();
            replacedStr.replace(startIndex, endIndex, replacedMatches.get(i));
        }
        return replacedStr.toString().replaceAll("\\[", "").replaceAll("\\]", "");
    }

    /**
     * 替换字符串,不区分大小写
     *
     * @param original 原始字符串
     * @param from     要替换的字符串
     * @param to       替换后的字符串
     * @return 替换后的字符串
     */
    public static String replaceIgnoreCase(String original, String from, String to) {
        // 将要查找的字符串转换为正则表达式形式,并添加不区分大小写的标志(?i)
        String regex = "(?i)" + Pattern.quote(from);

        // 使用正则表达式进行替换
        return original.replaceAll(regex, to);
    }

    /**
     * 文件处理
     *
     * @param file       要处理的文件夹
     * @param ignoreName 忽略文件名
     * @return 处理的文件数量
     */
    public static long judgeDocumentPath(File file, String ignoreName, LocalDate nowDate, String rule) {
        long num = 0;
        File[] files = file.listFiles();
        for (File file1 : files) {
            if (file1.isDirectory()) {
                //调用方法循环完成对文件夹中文件的删除
                Pattern PATH_RELE_PATTERN = Pattern.compile(rule.replaceAll("[/\\\\]", "[/\\\\\\\\]").replace("yyyy", "(\\d{4})").replace("MM", "(\\d{2})").replace("dd", "(\\d{2})"));
                Matcher matcher = PATH_RELE_PATTERN.matcher(file1.getAbsolutePath());
                if (matcher.find()) {
                    LocalDate pathDate = dateConversion(matcher.group(), rule);
                    long betweenDays = ChronoUnit.DAYS.between(pathDate, nowDate);
                    if (betweenDays > days) {
                        num = num + deleteFolderPath(file1, ignoreName);
                    }
                } else {
                    //调用方法循环完成对文件夹中文件的删除
                    num = num + judgeDocumentPath(file1, ignoreName, nowDate, rule);
                }
            }
        }
        return num;
    }

    /**
     * 判断文件名中是否包含关键字,如果包含则剔除
     *
     * @param notLikeStr
     * @param fileName
     * @return
     */
    public static boolean getIsNotLike(String notLikeStr, String fileName) {
        String[] strArr = notLikeStr.split(";");
        for (int i = 0; i < strArr.length; i++) {
            if (fileName.contains(strArr[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * 删除文件夹下文件
     *
     * @param file       要删除的文件夹
     * @param ignoreName 忽略文件名
     * @return 删除的文件数量
     */
    public static long deleteFolderPath(File file, String ignoreName) {
        AtomicInteger i = new AtomicInteger();
        // 路径
        try (Stream<Path> stream = Files.walk(file.toPath())
                .filter(path -> !path.toFile().isDirectory() && !getIsNotLike(ignoreName, path.getFileName().toString()))
                .sorted(Comparator.reverseOrder())) {
            stream.forEach(path -> {
//                        try {
                        Files.delete(path);
//                            if (Files.deleteIfExists(path)) {
                        i.incrementAndGet();
//                            }
//                        } catch (IOException e) {
//                            throw new RuntimeException(e);
//                        }
                    }
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
        return i.get();
    }

    /**
     * 删除文件夹下文件
     *
     * @param file       要删除的文件夹
     * @param ignoreName 忽略文件名
     * @return 删除的文件数量
     */
    public static long deleteFolder(File file, String ignoreName) {
        double nowTime = System.currentTimeMillis();
        AtomicInteger i = new AtomicInteger();
        // 路径
        try (Stream<Path> stream = Files.walk(file.toPath())
                .sorted(Comparator.reverseOrder())) {
            stream.forEachOrdered(path -> {
                try {
                    //最后修改时间
                    Date savaTime = new Date(path.toFile().lastModified());
                    double saveTimes = savaTime.getTime();
                    //获取非文件夹获取修改时间,因为删除文件时,会修改文件夹的最后修改时间,导致无法删除
                    if (!path.toFile().isDirectory() && !getIsNotLike(ignoreName, path.getFileName().toString()) && nowTime - saveTimes > (double) days * dayTimes) {
                        boolean deleteSuccess = Files.deleteIfExists(path);
                        if (deleteSuccess) {
                            i.incrementAndGet();
                        }
                    } else if (path.toFile().listFiles().length == 0) {
                        Files.deleteIfExists(path);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return i.get();
    }

    /**
     * 转换日期格式,支持格式为:formats
     *
     * @param path 路径
     * @return LocalDate 转换后的日期
     */
    public static LocalDate dateConversion(String path, String rule) {
        String[] formats = {rule.replace("\\", "/"), rule.replace("/", "\\")};
        for (String format : formats) {
            try {
                LocalDate localDate = LocalDate.parse(path, DateTimeFormatter.ofPattern(format));
                if (localDate != null) {
                    return localDate;
                }
            } catch (DateTimeParseException e) {
                // 没有找到有效的日期
            }
        }
        return null;
    }
}

3. 问题调整

3.1 如需严格按照文件名称删除,在正则里添加必须按正则结尾去处理

例子:之前的代码会删除如 20240505-副本的文件夹,更新成新的后,将不会删除

Pattern PATH_RELE_PATTERN = Pattern.compile("(" + rule.replaceAll("[/\\\\]", "[/\\\\\\\\]").replace("yyyy", "(\\d{4})").replace("MM", "(\\d{2})").replace("dd", "(\\d{2})") + ")$");
                    
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐