web-dev-qa-db-fra.com

Convertir le chemin du fichier en un URI de fichier?

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.

190
Tinister

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;
276
JaredPar

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.

33
poizan42

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

8
MrCalvin

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"

7
Bob Stine

Au moins dans .NET 4.5+, vous pouvez également effectuer les tâches suivantes:

var uri = new System.Uri("C:\\foo", UriKind.Absolute);
3
Gavin Greenwalt

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.

1
IllidanS4

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

0
ThingsHappen