c# - Convierte la ruta del archivo a un archivo URI?
uri path c# (6)
Al menos en .NET 4.5+ también puedes hacer:
var uri = new System.Uri("C://foo", UriKind.Absolute);
¿El .NET Framework tiene algún método para convertir una ruta (por ejemplo, "C:/whatever.txt"
) en un archivo URI (por ejemplo, "file:///C:/whatever.txt"
)?
La clase System.Uri tiene el reverso (desde un URI de archivo a la ruta absoluta), pero nada por lo que puedo encontrar para convertir a un URI de archivo.
Además, esta no es una aplicación ASP.NET.
El constructor System.Uri
tiene la capacidad de analizar rutas completas de archivos y convertirlas en rutas de estilo URI. Entonces puedes hacer lo siguiente:
var uri = new System.Uri("c://foo");
var converted = uri.AbsoluteUri;
Las soluciones anteriores no funcionan en Linux.
Al usar .NET Core, intentar ejecutar un 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 algunos consejos 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"
Lo que nadie parece darse cuenta es que ninguno de los constructores System.Uri
maneja correctamente ciertas rutas con signos de porcentaje en ellas.
new Uri(@"C:/%51.txt").AbsoluteUri;
Esto le da "file:///C:/Q.txt"
lugar de "file:///C:/%2551.txt"
.
Ninguno de los valores del argumento dontEscape obsoleto hace ninguna diferencia, y al especificar el UriKind también se obtiene el mismo resultado. Probar con UriBuilder tampoco ayuda:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:/%51.txt" }.Uri.AbsoluteUri
Esto también devuelve "file:///C:/Q.txt"
.
Por lo que puedo decir, el marco realmente no tiene ninguna forma de hacerlo correctamente.
Podemos intentarlo reemplazando las barras diagonales inversas por barras diagonales y Uri.EscapeUriString
la ruta a Uri.EscapeUriString
, es decir,
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, ''/''))).AbsoluteUri
Esto parece funcionar al principio, pero si le das la ruta C:/a b.txt
entonces terminas con file:///C:/a%2520b.txt
lugar de file:///C:/a%20b.txt
- de alguna manera, 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
en cuenta - lo que parece ser generalmente aceptado en Windows es convertirlas en pseudo- urls del file://remote/share/foo.txt
formulario file://remote/share/foo.txt
, por lo que deberíamos tener eso en cuenta también.
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. 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 ser la forma en que normalmente 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.
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 trazador de líneas:
New Uri("D:/Development/~AppFolder/Att/1.gif").AbsoluteUri
Salida :
file:///D:/Development/~AppFolder/Att/1.gif
UrlCreateFromPath al rescate! Bueno, no del todo, ya que no admite formatos extendidos y de ruta 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 comience con un prefijo especial, se elimina. 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.
Una observación muy interesante que tuve es que mientras "// device / path" se transforma correctamente en "file: // device / path", específicamente "// localhost / path" se transforma en solo "file: /// path" .
La función WinApi logró codificar caracteres especiales, pero deja los caracteres específicos de Unicode sin codificar, a diferencia del construtor de Uri . En ese caso, AbsoluteUri contiene la URL correctamente codificada, mientras que OriginalString se puede utilizar para conservar los caracteres Unicode.