Lo que nadie parece darse cuenta es que ninguno de los constructores System.Uri
maneja correctamente ciertos caminos con el por ciento signos en ellos.
new Uri(@"C:\%51.txt").AbsoluteUri;
Esto le da "file:///C:/Q.txt"
en lugar de "file:///C:/%2551.txt"
.
Ninguno de los valores del argumento dontEscape obsoleto hace alguna diferencia, y al especificar el UriKind también se obtiene el mismo resultado. Tratando con la UriBuilder tampoco ayuda:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri
Esto devuelve "file:///C:/Q.txt"
también.
Por lo que puedo decir, el marco realmente no tiene ninguna forma de hacerlo correctamente.
Podemos tratar de que mediante la sustitución de las barras invertidas con barras diagonales y alimentar el camino a Uri.EscapeUriString
- es decir
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
Esto parece funcionar al principio, pero si le das el camino C:\a b.txt
entonces terminan con file:///C:/a%2520b.txt
en lugar de file:///C:/a%20b.txt
- de alguna manera se decide que algunas secuencias deben decodificarse pero no otras. Ahora podríamos simplemente prefijar con "file:///"
nosotros mismos, sin embargo, esto no toma en cuenta las rutas UNC como \\remote\share\foo.txt
- lo que parece ser generalmente aceptado en Windows es convertirlas en pseudo-urls del formulario file://remote/share/foo.txt
, por lo que debemos tenerlo en cuenta como bien.
EscapeUriString
también tiene el problema de que no escapa al carácter '#'
. En este punto, parece que no tenemos otra opción que hacer nuestro propio método desde cero. Así que esto es lo que sugieren:
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();
}
Esto deja intencionalmente + y: sin codificar, ya que parece ser la forma en que generalmente se hace en Windows. También solo codifica latin1, ya que Internet Explorer no puede entender los caracteres Unicode en las URL de los archivos si están codificados.
Y eso imprime el archivo: /// c:/foo ¿verdad? – knocte
Es decir, si "convertido" se imprime ... – knocte
'ruta var = new ("file: /// C: /whatever.txt") Uri .LocalPath;' convierte un URI de nuevo en una ruta de archivo local, también para cualquier persona eso necesita esto – Pondidum