2009-10-09 45 views
157

¿Tiene .NET Framework algún método para convertir una ruta (por ejemplo, "C:\whatever.txt") en un URI de archivo (por ejemplo, "file:///C:/whatever.txt")?¿Convertir la ruta del archivo a un archivo URI?

La clase System.Uri tiene el orden inverso (desde un URI de archivo a la ruta absoluta), pero no hay nada que pueda encontrar para convertirlo a un URI de archivo.

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

Respuesta

232

El constructor System.Uri tiene la capacidad de analizar rutas completas de archivos y convertirlas en rutas de estilo URI. Por lo que sólo puede hacer lo siguiente:

var uri = new System.Uri("c:\\foo"); 
var converted = uri.AbsoluteUri; 
+0

Y eso imprime el archivo: /// c:/foo ¿verdad? – knocte

+0

Es decir, si "convertido" se imprime ... – knocte

+62

'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

7

VB.NET:

Dim URI As New Uri("D:\Development\~AppFolder\Att\1.gif") 

diferentes salidas:

URI.AbsolutePath -> D:/Development/~AppFolder/Att/1.gif 
URI.AbsoluteUri -> file:///D:/Development/~AppFolder/Att/1.gif 
URI.OriginalString -> D:\Development\~AppFolder\Att\1.gif 
URI.ToString  -> file:///D:/Development/~AppFolder/Att/1.gif 
URI.LocalPath  -> D:\Development\~AppFolder\Att\1.gif 

Un forro:

New Uri("D:\Development\~AppFolder\Att\1.gif").AbsoluteUri 

salida: file:///D:/Development/~AppFolder/Att/1.gif

+2

'AbsoluteUri' es correcta, ya que codifica también espacios para% 20. – psulek

+0

Estoy convencido de que esto tiene los mismos problemas descritos en [la respuesta que habla sobre el manejo especial de caracteres] (http://stackoverflow.com/a/35734486/429091). – binki

3

Al menos en .NET 4.5+ también se puede hacer:

var uri = new System.Uri("C:\\foo", UriKind.Absolute); 
+1

¿No te arriesgas a obtener 'UriFormatException' algún día? – berezovskyi

+0

Esto tampoco funciona correctamente, 'new Uri (@" C: \% 51.txt ", UriKind.Absolute) .AbsoluteUri' devuelve ' "file: /// C: /Q.txt" 'en lugar de' "file: /// C: /%2551.txt" ' – poizan42

22

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.

+0

¿Hay algún nuget que incluya esto con una licencia liberal? Es una pena que no exista una forma adecuada de que esto exista en el marco y mantener copypasta actualizado también es difícil ... – binki

+1

Puede usar el código anterior bajo los términos de la licencia MIT (no creo que algo tan corto sea siquiera susceptible de protección por derecho de autor, pero ahora tienes una concesión explícita) – poizan42

1

Las soluciones anteriores no funcionan en Linux.

El uso de .NET Core, intentar ejecutar new Uri("/home/foo/README.md") resultados en 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) 
    ... 

que necesita para dar el CLR algunos consejos sobre qué tipo de URL que 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 funciona en Windows, también.

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

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

1

UrlCreateFromPath al rescate! Bueno, no del todo, ya que no es compatible con formatos de ruta extendida y UNC, pero eso no es tan difícil de superar:

public static Uri FileUrlFromPath(string path) 
{ 
    const string prefix = @"\\"; 
    const string extended = @"\\?\"; 
    const string extendedUnc = @"\\?\UNC\"; 
    const string device = @"\\.\"; 
    const StringComparison comp = StringComparison.Ordinal; 

    if(path.StartsWith(extendedUnc, comp)) 
    { 
     path = prefix+path.Substring(extendedUnc.Length); 
    }else if(path.StartsWith(extended, comp)) 
    { 
     path = prefix+path.Substring(extended.Length); 
    }else if(path.StartsWith(device, comp)) 
    { 
     path = prefix+path.Substring(device.Length); 
    } 

    int len = 1; 
    var buffer = new StringBuilder(len); 
    int result = UrlCreateFromPath(path, buffer, ref len, 0); 
    if(len == 1) Marshal.ThrowExceptionForHR(result); 

    buffer.EnsureCapacity(len); 
    result = UrlCreateFromPath(path, buffer, ref len, 0); 
    if(result == 1) throw new ArgumentException("Argument is not a valid path.", "path"); 
    Marshal.ThrowExceptionForHR(result); 
    return new Uri(buffer.ToString()); 
} 

[DllImport("shlwapi.dll", CharSet=CharSet.Auto, SetLastError=true)] 
static extern int UrlCreateFromPath(string path, StringBuilder url, ref int urlLength, int reserved); 

En caso de que la ruta comienza con un prefijo especial, que se retira. Aunque la documentación no lo menciona, la función genera la longitud de la URL, incluso si el búfer es más pequeño, así que primero obtengo la longitud y luego asigno el búfer.

Algunos muy interesante observación que tuve es que si bien "\\ dispositivo \ ruta" se transforma correctamente a "file: // dispositivo/ruta", específicamente "\\ localhost \ ruta" se transforma en solo archivo" :///camino".

La función WinApi logró codificar caracteres especiales, pero deja caracteres Unicode no codificados, a diferencia del Uri construtor. En ese caso, absoluteURI contiene la URL codificada correspondiente, mientras que OriginalString se puede utilizar para retener los caracteres Unicode.

Cuestiones relacionadas