Здається, ніхто не усвідомлює, що жоден із System.Uri
конструкторів правильно не обробляє певні контури зі знаками відсотків.
new Uri(@"C:\%51.txt").AbsoluteUri;
Це дає вам "file:///C:/Q.txt"
замість "file:///C:/%2551.txt"
.
Ні значення застарілого аргументу dontEscape не має жодних змін, і вказівка UriKind також не дає того ж результату. Спроба з UriBuilder також не допомагає:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri
Це також повертається "file:///C:/Q.txt"
.
Наскільки я можу сказати, що в рамках насправді бракує будь-якого способу зробити це правильно.
Ми можемо спробувати це, замінивши звороту косу рису косою нахилом вперед і подати шлях до Uri.EscapeUriString
- тобто
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
Здається, це працює спочатку, але якщо ви надаєте йому шлях, C:\a b.txt
то ви закінчуєте file:///C:/a%2520b.txt
замість цього file:///C:/a%20b.txt
- якимось чином він вирішує, що деякі послідовності слід розшифрувати, а не інші. Тепер ми можемо просто "file:///"
встановити приставку , але це не може врахувати такі шляхи UNC, як, \\remote\share\foo.txt
мабуть, загальноприйняте в Windows - це перетворити їх у псевдо-URL-адреси форми file://remote/share/foo.txt
, тому ми також повинні це врахувати.
EscapeUriString
також є проблема, що вона не уникає '#'
персонажа. Здається, на даний момент у нас немає іншого вибору, окрім як зробити власний метод з нуля. Ось що я пропоную:
public static string FilePathToFileUrl(string filePath)
{
StringBuilder uri = new StringBuilder();
foreach (char v in filePath)
{
if ((v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z') || (v >= '0' && v <= '9') ||
v == '+' || v == '/' || v == ':' || v == '.' || v == '-' || v == '_' || v == '~' ||
v > '\xFF')
{
uri.Append(v);
}
else if (v == Path.DirectorySeparatorChar || v == Path.AltDirectorySeparatorChar)
{
uri.Append('/');
}
else
{
uri.Append(String.Format("%{0:X2}", (int)v));
}
}
if (uri.Length >= 2 && uri[0] == '/' && uri[1] == '/') // UNC path
uri.Insert(0, "file:");
else
uri.Insert(0, "file:///");
return uri.ToString();
}
Це навмисно залишає + і: незашифровано, як це здається, як це зазвичай робиться в Windows. Він також кодує лише latin1, оскільки Internet Explorer не може зрозуміти символи unicode у URL-адресах файлів, якщо вони закодовані.
var path = new Uri("file:///C:/whatever.txt").LocalPath;
перетворює Uri назад у локальну файлову дорогу для тих, хто цього потребує.