Ce que personne ne semble réaliser, c'est qu'aucun des System.Uri
constructeurs 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 de différence, et la spécification de 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
Cela revient "file:///C:/Q.txt"
également.
Pour autant que je sache, le cadre manque réellement de moyen de le faire correctement.
Nous pouvons essayer en remplaçant les barres obliques inverses par des barres obliques et en alimentant 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
- d'une manière ou d'une autre, il décide que certaines séquences doivent être décodées mais pas d'autres. Maintenant, nous pourrions simplement préfixer avec "file:///"
nous-mêmes, mais cela ne prend pas en compte les chemins UNC \\remote\share\foo.txt
- ce qui semble généralement accepté sous Windows est de les transformer en pseudo-URL du formulaire file://remote/share/foo.txt
, nous devons donc en tenir compte également.
EscapeUriString
a aussi le problème qu'il n'échappe pas au '#'
personnage. Il semblerait à ce stade que nous n'avons pas d'autre choix que de créer notre propre méthode à partir de zéro. Voici donc ce que je propose:
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 + et: non encodé car cela semble être la façon dont cela se fait habituellement sous Windows. Il n'encode également que latin1 car Internet Explorer ne peut pas comprendre les caractères unicode dans les URL des fichiers s'ils sont encodés.
var path = new Uri("file:///C:/whatever.txt").LocalPath;
transforme également un Uri en chemin de fichier local pour tous ceux qui en ont besoin.