PathUtils.cs 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. namespace MeloongCore;
  2. public static class PathUtils {
  3. #region 分隔符
  4. /// <summary>
  5. /// 确保路径的结尾包含文件夹分隔符。
  6. /// </summary>
  7. public static string WithSeparator(string folder)
  8. => folder.EndsWithF(Path.DirectorySeparatorChar) ? folder : folder + Path.DirectorySeparatorChar;
  9. /// <summary>
  10. /// 确保路径的结尾不包含文件夹分隔符。
  11. /// </summary>
  12. public static string WithoutSeparator(string folder)
  13. => folder.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
  14. #endregion
  15. #region 长路径
  16. /// <summary>
  17. /// 若路径较长,则尽量将其转换为短路径。
  18. /// 若输入的是文件夹路径,不保证其结尾是否有文件夹分隔符。
  19. /// </summary>
  20. public static string ToShortPath(string fullName, bool keepFileName = false) {
  21. if (string.IsNullOrEmpty(fullName)) return fullName;
  22. fullName = PathUtils.WithoutLongPath( fullName.Replace('/', '\\'));
  23. if (fullName.Length <= 200) return fullName;
  24. // 保留文件名
  25. string pathToKeep = "";
  26. string pathToShorten = fullName;
  27. if (fullName.EndsWithF(".jar", true)) keepFileName = true; // jar 文件的文件名需要保留原样,否则会导致 Forge 1.20.1 无法通过文件名识别模块名
  28. if (keepFileName && FileUtils.Exists(fullName)) {
  29. pathToKeep = Path.GetFileName(fullName);
  30. pathToShorten = Path.GetDirectoryName(fullName);
  31. }
  32. // 逐级向上寻找已存在的文件夹,将不存在的部分挪到 suffix,不再缩短
  33. while (!DirectoryUtils.Exists(pathToShorten) && !FileUtils.Exists(pathToShorten)) { // 如果路径不存在
  34. string parentPath = Path.GetDirectoryName(pathToShorten);
  35. if (string.IsNullOrEmpty(parentPath) || parentPath == pathToShorten) return fullName; // 已经到达根目录,全都不存在,直接返回
  36. pathToKeep = Path.Combine(Path.GetFileName(pathToShorten), pathToKeep);
  37. pathToShorten = parentPath;
  38. }
  39. if (pathToShorten.Length <= 10) return fullName;
  40. // 缩短路径
  41. char[] buffer = new char[260];
  42. int result = GetShortPathNameW(pathToShorten, buffer, buffer.Length);
  43. if (result == 0) return fullName;
  44. string shortPath = new(buffer, 0, result);
  45. return Path.Combine(shortPath, pathToKeep);
  46. }
  47. [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
  48. private static extern int GetShortPathNameW(string lpszLongPath, [Out] char[] buffer, int cchBuffer);
  49. /// <summary>
  50. /// 将完整路径转换为以 <c>\\?\</c> 开头的标准长路径格式。
  51. /// 这会去除路径末尾的分隔符,且将 / 替换为 \。
  52. /// </summary>
  53. public static string WithLongPath(string path) {
  54. if (string.IsNullOrWhiteSpace(path)) return path;
  55. if (path.StartsWithF(@"\\?\")) return path; // 已经是长路径
  56. path = WithoutSeparator(path).Replace('/', '\\'); // API 要求这个格式……
  57. if (path.StartsWithF(@"\\")) {
  58. return $@"\\?\UNC\{path.Substring(2)}";
  59. } else {
  60. return $@"\\?\{path}";
  61. }
  62. }
  63. /// <summary>
  64. /// 去除路径开头的 <c>\\?\</c>。
  65. /// </summary>
  66. public static string WithoutLongPath(string path) {
  67. if (path.StartsWithF(@"\\?\UNC\")) return @"\\" + path.AfterLast(@"\\?\UNC\");
  68. if (path.StartsWithF(@"\\?\")) return path.AfterLast(@"\\?\");
  69. return path;
  70. }
  71. #endregion
  72. }