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;
|
}
|
|
}
|