Ciò che nessuno sembra rendersi conto è che nessuno dei System.Uri
costruttori gestisce correttamente determinati percorsi con segni di percentuale in essi.
new Uri(@"C:\%51.txt").AbsoluteUri;
Questo ti dà "file:///C:/Q.txt"
invece di "file:///C:/%2551.txt"
.
Né i valori dell'argomento deprecato dontEscape fanno alcuna differenza, e specificando UriKind si ottiene anche lo stesso risultato. Provare con UriBuilder non aiuta neanche:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri
Anche questo ritorna "file:///C:/Q.txt"
.
Per quanto ne so, il framework in realtà non ha alcun modo di farlo correttamente.
Possiamo provarci sostituendo le barre rovesciate con barre in avanti e alimentare il percorso verso Uri.EscapeUriString
- ad es
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
Questo sembra funzionare all'inizio, ma se gli dai il percorso C:\a b.txt
allora finisci file:///C:/a%2520b.txt
invece di file:///C:/a%20b.txt
- in qualche modo decide che alcune sequenze dovrebbero essere decodificate ma non altre. Ora potremmo prefissarci solo con "file:///"
noi stessi, tuttavia questo non tiene \\remote\share\foo.txt
conto dei percorsi UNC come quello che sembra essere generalmente accettato su Windows è trasformarli in pseudo-url del modulo file://remote/share/foo.txt
, quindi dovremmo tenerne conto anche.
EscapeUriString
ha anche il problema che non sfugge al '#'
personaggio. A questo punto sembrerebbe che non abbiamo altra scelta che fare il nostro metodo da zero. Quindi questo è quello che suggerisco:
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();
}
Questo lascia intenzionalmente + e: non codificato in quanto sembra essere come di solito viene fatto su Windows. Codifica anche latin1 poiché Internet Explorer non è in grado di comprendere i caratteri unicode negli URL dei file se sono codificati.
var path = new Uri("file:///C:/whatever.txt").LocalPath;
trasforma un Uri in un percorso file locale anche per chiunque ne abbia bisogno.