Convertir le chemin du fichier en URI de fichier?

201

Le .NET Framework a-t-il des méthodes pour convertir un chemin (par exemple "C:\whatever.txt") en 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 pour autant que je puisse trouver pour convertir en URI de fichier.

En outre, ce n'est pas une application ASP.NET.

Tinister
la source

Réponses:

292

Le System.Uriconstructeur a la possibilité d'analyser les chemins de fichiers complets et de les transformer en chemins de style URI. Vous pouvez donc simplement faire ce qui suit:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
JaredPar
la source
78
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.
Pondidum
2
Comme une note. Ce type d'Uri est cliquable dans la sortie VS et la sortie des tests unitaires R # dans les fenêtres de session
AlfeG
7
Ce n'est malheureusement pas correct. Par exemple, new Uri(@"C:\%51.txt").AbsoluteUrivous donne "file:///C:/Q.txt"au lieu de"file:///C:/%2551.txt"
poizan42
2
cela ne fonctionnera pas avec le chemin avec des espaces, c'est-à-dire: "C: \ dossier de test \ que ce
soit.txt
Cela ne fonctionnera pas non plus avec les chemins qui contiennent un caractère #.
lewis
42

Ce que personne ne semble réaliser, c'est qu'aucun des System.Uriconstructeurs 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.txtvous vous retrouvez avec file:///C:/a%2520b.txtau 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.

EscapeUriStringa 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.

poizan42
la source
Y a-t-il une pépite qui inclut cela avec une licence libérale? Dommage qu'il n'y ait pas de moyen approprié pour que cela existe dans le cadre et maintenir la mise à jour de copypasta est difficile aussi…
binki
4
Vous pouvez utiliser le code ci-dessus sous les termes de la licence MIT (je ne crois pas que quelque chose d'aussi court devrait même être protégé par le droit d'auteur, mais maintenant vous avez une
autorisation
9

Les solutions ci-dessus ne fonctionnent pas sous Linux.

À l'aide de .NET Core, la tentative d'exécution 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 conseils sur le type d'URL dont vous disposez.

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 également sur Windows.

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

... émet "file:///C:/Users/foo/README.md"

Bob Stine
la source
Si vous savez que le chemin est absolu, vous pouvez utilisernew Uri("/path/to/file", UriKind.Absolute);
Minijack
8

VB.NET:

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

Différentes sorties:

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

MrCalvin
la source
2
AbsoluteUriest correct car il code également les espaces en% 20.
psulek
Je suis convaincu que cela souffre des mêmes problèmes décrits dans la réponse qui parle de la gestion des caractères spéciaux .
binki
4

Au moins dans .NET 4.5+, vous pouvez également faire:

var uri = new System.Uri("C:\\foo", UriKind.Absolute);
Gavin Greenwalt
la source
1
Ne risquez-vous pas d'en avoir un UriFormatExceptionun jour?
berezovskyi
Cela ne fonctionne pas correctement non plus, new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUriretourne "file:///C:/Q.txt"au lieu de"file:///C:/%2551.txt"
poizan42
2

UrlCreateFromPath à la rescousse! Eh bien, pas entièrement, car il ne prend pas en charge les formats de chemin étendus 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, donc j'obtiens d'abord la longueur puis alloue le tampon.

Certains très observation intéressante que j'ai faite est que tandis que "\\ device \ path" est correctement transformé en "file: // device / path", spécifiquement "\\ localhost \ path" est transformé en seulement "file: /// path" .

La fonction WinApi a réussi à coder les 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 encodée, tandis que OriginalString peut être utilisé pour conserver les caractères Unicode.

IllidanS4 veut récupérer Monica
la source
0

La solution de contournement est simple. Utilisez simplement la méthode Uri (). ToString () et codez en pourcentage les espaces blancs, le cas échéant, par la suite.

string path = new Uri("C:\my exampleㄓ.txt").ToString().Replace(" ", "%20");

renvoie correctement le fichier: /// C: / mon% 20exemple ㄓ .txt

Des choses arrivent
la source