Nikt nie zdaje sobie sprawy z tego, że żaden z System.Uri
konstruktorów nie obsługuje poprawnie niektórych ścieżek ze znakami procentowymi.
new Uri(@"C:\%51.txt").AbsoluteUri;
To daje "file:///C:/Q.txt"
zamiast "file:///C:/%2551.txt"
.
Żadna z wartości przestarzałego argumentu dontEscape nie robi żadnej różnicy, a określenie UriKind daje również ten sam wynik. Próbowanie z UriBuilder nie pomaga:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri
To również powraca "file:///C:/Q.txt"
.
O ile mogę stwierdzić, frameworkowi brakuje właściwie żadnego sposobu, aby to zrobić poprawnie.
Możemy tego spróbować, zastępując ukośniki odwrotne ukośnikami i kierując ścieżkę do Uri.EscapeUriString
- tj
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
Z początku wydaje się, że to działa, ale jeśli podasz ścieżkę, C:\a b.txt
to file:///C:/a%2520b.txt
zamiast tego file:///C:/a%20b.txt
- w jakiś sposób decyduje, że niektóre sekwencje powinny zostać zdekodowane, a inne nie. Teraz możemy po prostu przedrostek ze "file:///"
sobą, jednak nie bierze to \\remote\share\foo.txt
pod uwagę ścieżek UNC, jak się wydaje - ogólnie wydaje się, że w systemie Windows powszechnie przyjmuje się przekształcanie ich w pseudo-adresy URL formularza file://remote/share/foo.txt
, dlatego też powinniśmy to wziąć pod uwagę.
EscapeUriString
ma również problem polegający na tym, że nie ucieka '#'
postaci. Wydaje się w tym momencie, że nie mamy innego wyjścia, jak stworzyć własną metodę od zera. Oto co sugeruję:
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();
}
To celowo pozostawia + i: niezakodowane, ponieważ wydaje się, że tak zwykle dzieje się w systemie Windows. Koduje także Latin1, ponieważ Internet Explorer nie może zrozumieć znaków Unicode w adresach URL plików, jeśli są one zakodowane.
var path = new Uri("file:///C:/whatever.txt").LocalPath;
zamienia Uri z powrotem w lokalną ścieżkę plików dla każdego, kto tego potrzebuje.