2026-06-30 24681c81c09022f584a57006f2534b5f74723414
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package cn.iocoder.yudao.module.infra.framework.file.core.utils;
 
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
 
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;
 
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_PATH_INVALID;
 
/**
 * 文件路径工具类
 *
 * @author 芋道源码
 */
public class FilePathUtils {
 
    private FilePathUtils() {
    }
 
    /**
     * 校验文件名是否合法,禁止携带目录路径。
     *
     * @param name 文件名
     * @return 文件名
     */
    public static String validateFileName(String name) {
        if (StrUtil.isEmpty(name)) {
            return name;
        }
        if (!isPathValid(name) || StrUtil.contains(name, StrUtil.SLASH) || !StrUtil.equals(name, FileUtil.getName(name))) {
            throw exception(FILE_PATH_INVALID);
        }
        return name;
    }
 
    /**
     * 校验文件目录是否合法。
     *
     * @param directory 文件目录,允许为空
     * @return 是否合法
     */
    public static boolean isDirectoryValid(String directory) {
        return StrUtil.isEmpty(directory) || isPathValid(directory);
    }
 
    /**
     * 校验文件目录是否合法,不合法时抛出业务异常。
     *
     * @param directory 文件目录,允许为空
     */
    public static void validateDirectory(String directory) {
        if (!isDirectoryValid(directory)) {
            throw exception(FILE_PATH_INVALID);
        }
    }
 
    /**
     * 校验文件相对路径是否合法,不合法时抛出业务异常。
     *
     * @param path 文件相对路径
     */
    public static void validatePath(String path) {
        if (StrUtil.isEmpty(path) || !isPathValid(path)) {
            throw exception(FILE_PATH_INVALID);
        }
    }
 
    /**
     * 校验路径是否为安全的相对路径,禁止绝对路径、Windows 盘符、反斜杠、空路径段和目录穿越。
     *
     * @param path 路径
     * @return 是否合法
     */
    private static boolean isPathValid(String path) {
        // 不能以 / 或 \ 开头,避免传入绝对路径
        if (StrUtil.startWithAny(path, StrUtil.SLASH, "\\")) {
            return false;
        }
        // 不能包含反斜杠或空字符,避免绕过不同系统的路径解析
        if (StrUtil.contains(path, "\\") || path.indexOf('\0') >= 0) {
            return false;
        }
        // 不能是 Windows 盘符路径,例如 C:/test.jpg
        if (path.length() >= 2 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') {
            return false;
        }
        try {
            // 使用 JDK Path 再兜底判断一次绝对路径
            if (Paths.get(path).isAbsolute()) {
                return false;
            }
        } catch (InvalidPathException ex) {
            return false;
        }
        // 不能包含空路径段、当前目录或上级目录,避免目录穿越
        for (String segment : path.split(StrUtil.SLASH, -1)) {
            if (StrUtil.isEmpty(segment) || ".".equals(segment) || "..".equals(segment)) {
                return false;
            }
        }
        return true;
    }
 
}