¿Convertir la ruta del archivo en un URI de archivo?

Resuelto Tinister asked hace 15 años • 8 respuestas

¿Tiene .NET Framework algún método para convertir una ruta (p. ej. "C:\whatever.txt") en un URI de archivo (p. ej. "file:///C:/whatever.txt")?

La clase System.Uri tiene lo contrario (de un URI de archivo a una ruta absoluta), pero hasta donde puedo encontrar nada para convertir a un URI de archivo.

Además, esta no es una aplicación ASP.NET.

Tinister avatar Oct 10 '09 05:10 Tinister
Aceptado

El System.Uriconstructor tiene la capacidad de analizar rutas de archivos completas y convertirlas en rutas de estilo URI. Entonces puedes hacer lo siguiente:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
JaredPar avatar Oct 09 '2009 22:10 JaredPar

Lo que nadie parece darse cuenta es que ninguno de los System.Uriconstructores maneja correctamente ciertas rutas con signos de porcentaje.

new Uri(@"C:\%51.txt").AbsoluteUri;

Esto te da "file:///C:/Q.txt"en lugar de "file:///C:/%2551.txt".

Ninguno de los valores del obsoleto argumento dontEscape hace ninguna diferencia, y especificar UriKind también da el mismo resultado. Probar con UriBuilder tampoco ayuda:

new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri

Esto "file:///C:/Q.txt"también regresa.

Hasta donde puedo decir, al marco le falta alguna forma de hacer esto correctamente.

Podemos intentarlo reemplazando las barras invertidas con barras diagonales y alimentando la ruta a, Uri.EscapeUriStringes decir

new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri

Esto parece funcionar al principio, pero si le da la ruta C:\a b.txt, termina con file:///C:/a%2520b.txten lugar de file:///C:/a%20b.txt: de alguna manera decide que algunas secuencias deben decodificarse pero no otras. Ahora podríamos simplemente anteponernos a "file:///"nosotros mismos, sin embargo, esto no toma \\remote\share\foo.txten cuenta las rutas UNC; lo que parece ser generalmente aceptado en Windows es convertirlas en pseudo-URL del formulario file://remote/share/foo.txt, por lo que también debemos tener eso en cuenta.

EscapeUriStringTambién tiene el problema de que no se le escapa al '#'personaje. Parecería que a estas alturas no nos queda más remedio que crear nuestro propio método desde cero. Entonces esto es lo que sugiero:

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 que así se hace normalmente 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.

poizan42 avatar Mar 01 '2016 22:03 poizan42

Las soluciones anteriores no funcionan en Linux.

Al utilizar .NET Core, intentar ejecutar new Uri("/home/foo/README.md")da como resultado una excepción:

Unhandled Exception: System.UriFormatException: Invalid URI: The format of the URI could not be determined.
   at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
   at System.Uri..ctor(String uriString)
   ...

Debe darle al CLR algunas pistas sobre qué tipo de URL tiene.

Esto funciona:

Uri fileUri = new Uri(new Uri("file://"), "home/foo/README.md");

...y la cadena devuelta por fileUri.ToString()es"file:///home/foo/README.md"

Esto también funciona en Windows.

new Uri(new Uri("file://"), @"C:\Users\foo\README.md").ToString()

...emite"file:///C:/Users/foo/README.md"

Bob Stine avatar Jan 16 '2017 14:01 Bob Stine