Le .NET Framework a-t-il des méthodes pour convertir un chemin (par exemple "C:\whatever.txt"
) en un URI de fichier (par exemple "file:///C:/whatever.txt"
)?
La classe System.Uri a l'inverse (d'un URI de fichier à un chemin absolu), mais rien que je puisse trouver pour la conversion en un URI de fichier.
En outre, ceci n'est pas une application ASP.NET.
Le constructeur System.Uri
peut analyser des chemins de fichiers complets et les transformer en chemins de style URI. Donc, vous pouvez simplement faire ce qui suit:
var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
Ce que personne ne semble se rendre compte, c’est qu’aucun des constructeurs System.Uri
ne gère correctement certains chemins contenant des signes de pourcentage.
new Uri(@"C:\%51.txt").AbsoluteUri;
Cela vous donne "file:///C:/Q.txt"
au lieu de "file:///C:/%2551.txt"
.
Aucune des valeurs de l'argument obsolète dontEscape ne fait aucune différence, et spécifier UriKind donne également le même résultat. Essayer avec UriBuilder n'aide pas non plus:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri
Ceci retourne également "file:///C:/Q.txt"
.
Autant que je sache, le cadre manque réellement de tout moyen de le faire correctement.
Nous pouvons essayer en remplaçant les barres obliques inverses par des barres obliques et en indiquant le chemin vers Uri.EscapeUriString
- c.-à-d.
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
Cela semble fonctionner au début, mais si vous lui donnez le chemin C:\a b.txt
, vous vous retrouvez avec file:///C:/a%2520b.txt
au lieu de file:///C:/a%20b.txt
- en quelque sorte, il décide que certains séquences devrait être décodé mais pas les autres. Nous pourrions maintenant préfixer "file:///"
nous-mêmes, mais cela ne prend pas en compte les chemins UNC tels que \\remote\share\foo.txt
- ce qui semble être généralement accepté sous Windows est de les transformer en pseudo-URL de la forme file://remote/share/foo.txt
, nous devrions donc en tenir compte également.
EscapeUriString
a également le problème qu'il n'échappe pas au caractère '#'
. Il semblerait à ce stade que nous n’avons pas d’autre choix que de créer notre propre méthode à partir de rien. Alors voici ce que je suggère:
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();
}
Cela laisse intentionnellement les + et les: non codés, comme cela semble être le cas avec Windows. De plus, il ne code que latin1, car Internet Explorer ne peut pas comprendre les caractères unicode dans les URL de fichier s'ils sont codés.
VB.NET:
Dim URI As New Uri("D:\Development\~AppFolder\Att\1.gif")
Différents résultats:
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
Bon mot:
New Uri("D:\Development\~AppFolder\Att\1.gif").AbsoluteUri
Sortie:
file:///D:/Development/~AppFolder/Att/1.gif
Les solutions ci-dessus ne fonctionnent pas sous Linux.
L'utilisation de .NET Core pour tenter d'exécuter new Uri("/home/foo/README.md")
entraîne une exception:
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)
...
Vous devez donner au CLR quelques indications sur le type d’URL que vous avez.
Cela marche:
Uri fileUri = new Uri(new Uri("file://"), "home/foo/README.md");
... et la chaîne renvoyée par fileUri.ToString()
est "file:///home/foo/README.md"
Cela fonctionne aussi sous Windows.
new Uri(new Uri("file://"), @"C:\Users\foo\README.md").ToString()
... émet "file:///C:/Users/foo/README.md"
Au moins dans .NET 4.5+, vous pouvez également effectuer les tâches suivantes:
var uri = new System.Uri("C:\\foo", UriKind.Absolute);
rlCreateFromPath à la rescousse! Eh bien, pas tout à fait, car il ne prend pas en charge les formats de chemin étendu et UNC, mais ce n'est pas si difficile à surmonter:
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);
Si le chemin commence par un préfixe spécial, il est supprimé. Bien que la documentation ne le mentionne pas, la fonction affiche la longueur de l'URL, même si le tampon est plus petit. J'obtiens donc d'abord la longueur, puis alloue le tampon.
Voici une observation intéressante très intéressante: bien que "\\ périphérique\chemin" soit correctement transformé en "fichier: // périphérique/chemin", en particulier " \\ localhost\path "est transformé en" fichier: /// path ".
La fonction WinApi a réussi à coder des caractères spéciaux, mais laisse les caractères spécifiques à Unicode non codés, contrairement au constructeur Uri . Dans ce cas, AbsoluteUri contient l'URL correctement codée, alors que OriginalString peut être utilisé pour conserver les caractères Unicode.
La solution de contournement est simple. Utilisez simplement la méthode Uri (). ToString () et les espaces de code pour cent, le cas échéant, par la suite.
string path = new Uri("C:\my exampleㄓ.txt").ToString().Replace(" ", "%20");
renvoie correctement fichier: /// C:/mon% 20exemple ㄓ .txt