Linux and OSX directory length limits
This commit is contained in:
parent
6901b8be35
commit
f822a23daa
@ -68,7 +68,7 @@ namespace FileManager
|
|||||||
///a choice made by the linux kernel. As best as I can tell, pretty
|
///a choice made by the linux kernel. As best as I can tell, pretty
|
||||||
//much everyone uses UTF-8.
|
//much everyone uses UTF-8.
|
||||||
int getFilesystemStringLength(StringBuilder str)
|
int getFilesystemStringLength(StringBuilder str)
|
||||||
=> LongPath.PlatformID is PlatformID.Win32NT ?
|
=> LongPath.IsWindows ?
|
||||||
str.Length
|
str.Length
|
||||||
: Encoding.UTF8.GetByteCount(str.ToString());
|
: Encoding.UTF8.GetByteCount(str.ToString());
|
||||||
|
|
||||||
|
|||||||
@ -6,11 +6,14 @@ using System.Text.RegularExpressions;
|
|||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Polly;
|
using Polly;
|
||||||
using Polly.Retry;
|
using Polly.Retry;
|
||||||
|
using Dinah.Core.Collections.Generic;
|
||||||
|
|
||||||
namespace FileManager
|
namespace FileManager
|
||||||
{
|
{
|
||||||
public static class FileUtility
|
public static class FileUtility
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "txt" => ".txt"
|
/// "txt" => ".txt"
|
||||||
/// <br />".txt" => ".txt"
|
/// <br />".txt" => ".txt"
|
||||||
@ -55,15 +58,15 @@ namespace FileManager
|
|||||||
|
|
||||||
// ensure uniqueness and check lengths
|
// ensure uniqueness and check lengths
|
||||||
var dir = Path.GetDirectoryName(path);
|
var dir = Path.GetDirectoryName(path);
|
||||||
dir = dir?.Truncate(LongPath.MaxDirectoryLength) ?? string.Empty;
|
dir = dir?.TruncateFilename(LongPath.MaxDirectoryLength) ?? string.Empty;
|
||||||
|
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path);
|
||||||
|
|
||||||
var filename = Path.GetFileNameWithoutExtension(path).Truncate(LongPath.MaxFilenameLength - extension.Length);
|
var filename = Path.GetFileNameWithoutExtension(path).TruncateFilename(LongPath.MaxFilenameLength - extension.Length);
|
||||||
var fileStem = Path.Combine(dir, filename);
|
var fileStem = Path.Combine(dir, filename);
|
||||||
|
|
||||||
|
|
||||||
var fullfilename = fileStem.Truncate(LongPath.MaxPathLength - extension.Length) + extension;
|
var fullfilename = fileStem.TruncateFilename(LongPath.MaxPathLength - extension.Length) + extension;
|
||||||
|
|
||||||
fullfilename = removeInvalidWhitespace(fullfilename);
|
fullfilename = removeInvalidWhitespace(fullfilename);
|
||||||
|
|
||||||
@ -71,7 +74,7 @@ namespace FileManager
|
|||||||
while (File.Exists(fullfilename) && !returnFirstExisting)
|
while (File.Exists(fullfilename) && !returnFirstExisting)
|
||||||
{
|
{
|
||||||
var increm = $" ({++i})";
|
var increm = $" ({++i})";
|
||||||
fullfilename = fileStem.Truncate(LongPath.MaxPathLength - increm.Length - extension.Length) + increm + extension;
|
fullfilename = fileStem.TruncateFilename(LongPath.MaxPathLength - increm.Length - extension.Length) + increm + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullfilename;
|
return fullfilename;
|
||||||
@ -129,6 +132,18 @@ namespace FileManager
|
|||||||
|
|
||||||
public static string RemoveLastCharacter(this string str) => string.IsNullOrEmpty(str) ? str : str[..^1];
|
public static string RemoveLastCharacter(this string str) => string.IsNullOrEmpty(str) ? str : str[..^1];
|
||||||
|
|
||||||
|
public static string TruncateFilename(this string filenameStr, int limit)
|
||||||
|
{
|
||||||
|
if (LongPath.IsWindows) return filenameStr.Truncate(limit);
|
||||||
|
|
||||||
|
int index = filenameStr.Length;
|
||||||
|
|
||||||
|
while (index > 0 && System.Text.Encoding.UTF8.GetByteCount(filenameStr, 0, index) > limit)
|
||||||
|
index--;
|
||||||
|
|
||||||
|
return filenameStr[..index];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move file.
|
/// Move file.
|
||||||
/// <br/>- Ensure valid file name path: remove invalid chars, ensure uniqueness, enforce max file length
|
/// <br/>- Ensure valid file name path: remove invalid chars, ensure uniqueness, enforce max file length
|
||||||
|
|||||||
@ -10,22 +10,51 @@ namespace FileManager
|
|||||||
{
|
{
|
||||||
//https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
|
//https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
|
||||||
|
|
||||||
public const int MaxDirectoryLength = MaxPathLength - 13;
|
|
||||||
public const int MaxPathLength = short.MaxValue;
|
|
||||||
public const int MaxFilenameLength = 255;
|
public const int MaxFilenameLength = 255;
|
||||||
|
public static readonly int MaxDirectoryLength;
|
||||||
|
public static readonly int MaxPathLength;
|
||||||
|
|
||||||
private const int MAX_PATH = 260;
|
static LongPath()
|
||||||
private const string LONG_PATH_PREFIX = @"\\?\";
|
{
|
||||||
|
if (IsWindows)
|
||||||
|
{
|
||||||
|
MaxPathLength = short.MaxValue;
|
||||||
|
MaxDirectoryLength = MaxPathLength - 13;
|
||||||
|
}
|
||||||
|
else if (IsOSX)
|
||||||
|
{
|
||||||
|
MaxPathLength = 1024;
|
||||||
|
MaxDirectoryLength = MaxPathLength - MaxFilenameLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MaxPathLength = 4096;
|
||||||
|
MaxDirectoryLength = MaxPathLength - MaxFilenameLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string Path { get; init; }
|
private const int WIN_MAX_PATH = 260;
|
||||||
|
private const string WIN_LONG_PATH_PREFIX = @"\\?\";
|
||||||
|
|
||||||
|
private LongPath(string path)
|
||||||
|
{
|
||||||
|
if (IsWindows && path.Length > MaxPathLength)
|
||||||
|
throw new System.IO.PathTooLongException($"Path exceeds {MaxPathLength} character limit. ({path})");
|
||||||
|
if ((!IsWindows && Encoding.UTF8.GetByteCount(path) > MaxPathLength))
|
||||||
|
throw new System.IO.PathTooLongException($"Path exceeds {MaxPathLength} byte limit. ({path})");
|
||||||
|
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
public string Path { get; }
|
||||||
public override string ToString() => Path;
|
public override string ToString() => Path;
|
||||||
|
|
||||||
internal static readonly PlatformID PlatformID = Environment.OSVersion.Platform;
|
internal static readonly bool IsWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
internal static readonly bool IsLinux = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||||
|
internal static readonly bool IsOSX = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||||
|
|
||||||
public static implicit operator LongPath(string path)
|
public static implicit operator LongPath(string path)
|
||||||
{
|
{
|
||||||
if (PlatformID is PlatformID.Unix) return new LongPath { Path = path };
|
if (!IsWindows) return new LongPath(path);
|
||||||
|
|
||||||
if (path is null) return null;
|
if (path is null) return null;
|
||||||
|
|
||||||
@ -33,15 +62,15 @@ namespace FileManager
|
|||||||
//the name to an NT-style name, except when using the "\\?\" prefix
|
//the name to an NT-style name, except when using the "\\?\" prefix
|
||||||
path = path.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
path = path.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
if (path.StartsWith(LONG_PATH_PREFIX))
|
if (path.StartsWith(WIN_LONG_PATH_PREFIX))
|
||||||
return new LongPath { Path = path };
|
return new LongPath(path);
|
||||||
else if ((path.Length > 2 && path[1] == ':') || path.StartsWith(@"UNC\"))
|
else if ((path.Length > 2 && path[1] == ':') || path.StartsWith(@"UNC\"))
|
||||||
return new LongPath { Path = LONG_PATH_PREFIX + path };
|
return new LongPath(WIN_LONG_PATH_PREFIX + path);
|
||||||
else if (path.StartsWith(@"\\"))
|
else if (path.StartsWith(@"\\"))
|
||||||
//The "\\?\" prefix can also be used with paths constructed according to the
|
//The "\\?\" prefix can also be used with paths constructed according to the
|
||||||
//universal naming convention (UNC). To specify such a path using UNC, use
|
//universal naming convention (UNC). To specify such a path using UNC, use
|
||||||
//the "\\?\UNC\" prefix.
|
//the "\\?\UNC\" prefix.
|
||||||
return new LongPath { Path = LONG_PATH_PREFIX + @"UNC\" + path.Substring(2) };
|
return new LongPath(WIN_LONG_PATH_PREFIX + @"UNC\" + path.Substring(2));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//These prefixes are not used as part of the path itself. They indicate that
|
//These prefixes are not used as part of the path itself. They indicate that
|
||||||
@ -50,9 +79,9 @@ namespace FileManager
|
|||||||
//a period to represent the current directory, or double dots to represent the
|
//a period to represent the current directory, or double dots to represent the
|
||||||
//parent directory. Because you cannot use the "\\?\" prefix with a relative
|
//parent directory. Because you cannot use the "\\?\" prefix with a relative
|
||||||
//path, relative paths are always limited to a total of MAX_PATH characters.
|
//path, relative paths are always limited to a total of MAX_PATH characters.
|
||||||
if (path.Length > MAX_PATH)
|
if (path.Length > WIN_MAX_PATH)
|
||||||
throw new System.IO.PathTooLongException();
|
throw new System.IO.PathTooLongException();
|
||||||
return new LongPath { Path = path };
|
return new LongPath(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +92,7 @@ namespace FileManager
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (PlatformID is PlatformID.Unix) return Path;
|
if (!IsWindows) return Path;
|
||||||
|
|
||||||
//Short Path names are useful for navigating to the file in windows explorer,
|
//Short Path names are useful for navigating to the file in windows explorer,
|
||||||
//which will not recognize paths longer than MAX_PATH. Short path names are not
|
//which will not recognize paths longer than MAX_PATH. Short path names are not
|
||||||
@ -103,7 +132,7 @@ namespace FileManager
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (PlatformID is PlatformID.Unix) return Path;
|
if (!IsWindows) return Path;
|
||||||
if (Path is null) return null;
|
if (Path is null) return null;
|
||||||
|
|
||||||
StringBuilder longPathBuffer = new(MaxPathLength);
|
StringBuilder longPathBuffer = new(MaxPathLength);
|
||||||
@ -117,9 +146,9 @@ namespace FileManager
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (PlatformID is PlatformID.Unix) return Path;
|
if (!IsWindows) return Path;
|
||||||
return
|
return
|
||||||
Path?.StartsWith(LONG_PATH_PREFIX) == true ? Path.Remove(0, LONG_PATH_PREFIX.Length)
|
Path?.StartsWith(WIN_LONG_PATH_PREFIX) == true ? Path.Remove(0, WIN_LONG_PATH_PREFIX.Length)
|
||||||
:Path;
|
:Path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user