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
|
||||
//much everyone uses UTF-8.
|
||||
int getFilesystemStringLength(StringBuilder str)
|
||||
=> LongPath.PlatformID is PlatformID.Win32NT ?
|
||||
=> LongPath.IsWindows ?
|
||||
str.Length
|
||||
: Encoding.UTF8.GetByteCount(str.ToString());
|
||||
|
||||
|
||||
@ -6,11 +6,14 @@ using System.Text.RegularExpressions;
|
||||
using Dinah.Core;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using Dinah.Core.Collections.Generic;
|
||||
|
||||
namespace FileManager
|
||||
{
|
||||
public static class FileUtility
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// "txt" => ".txt"
|
||||
/// <br />".txt" => ".txt"
|
||||
@ -55,15 +58,15 @@ namespace FileManager
|
||||
|
||||
// ensure uniqueness and check lengths
|
||||
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 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 fullfilename = fileStem.Truncate(LongPath.MaxPathLength - extension.Length) + extension;
|
||||
var fullfilename = fileStem.TruncateFilename(LongPath.MaxPathLength - extension.Length) + extension;
|
||||
|
||||
fullfilename = removeInvalidWhitespace(fullfilename);
|
||||
|
||||
@ -71,7 +74,7 @@ namespace FileManager
|
||||
while (File.Exists(fullfilename) && !returnFirstExisting)
|
||||
{
|
||||
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;
|
||||
@ -129,6 +132,18 @@ namespace FileManager
|
||||
|
||||
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>
|
||||
/// Move file.
|
||||
/// <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
|
||||
|
||||
public const int MaxDirectoryLength = MaxPathLength - 13;
|
||||
public const int MaxPathLength = short.MaxValue;
|
||||
public const int MaxFilenameLength = 255;
|
||||
public static readonly int MaxDirectoryLength;
|
||||
public static readonly int MaxPathLength;
|
||||
|
||||
private const int MAX_PATH = 260;
|
||||
private const string LONG_PATH_PREFIX = @"\\?\";
|
||||
static LongPath()
|
||||
{
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
if (PlatformID is PlatformID.Unix) return new LongPath { Path = path };
|
||||
if (!IsWindows) return new LongPath(path);
|
||||
|
||||
if (path is null) return null;
|
||||
|
||||
@ -33,15 +62,15 @@ namespace FileManager
|
||||
//the name to an NT-style name, except when using the "\\?\" prefix
|
||||
path = path.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
||||
|
||||
if (path.StartsWith(LONG_PATH_PREFIX))
|
||||
return new LongPath { Path = path };
|
||||
if (path.StartsWith(WIN_LONG_PATH_PREFIX))
|
||||
return new LongPath(path);
|
||||
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(@"\\"))
|
||||
//The "\\?\" prefix can also be used with paths constructed according to the
|
||||
//universal naming convention (UNC). To specify such a path using UNC, use
|
||||
//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
|
||||
{
|
||||
//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
|
||||
//parent directory. Because you cannot use the "\\?\" prefix with a relative
|
||||
//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();
|
||||
return new LongPath { Path = path };
|
||||
return new LongPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +92,7 @@ namespace FileManager
|
||||
{
|
||||
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,
|
||||
//which will not recognize paths longer than MAX_PATH. Short path names are not
|
||||
@ -103,7 +132,7 @@ namespace FileManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PlatformID is PlatformID.Unix) return Path;
|
||||
if (!IsWindows) return Path;
|
||||
if (Path is null) return null;
|
||||
|
||||
StringBuilder longPathBuffer = new(MaxPathLength);
|
||||
@ -117,9 +146,9 @@ namespace FileManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PlatformID is PlatformID.Unix) return Path;
|
||||
if (!IsWindows) return Path;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user