Dateipfad in Datei-URI konvertieren?

201

Verfügt das .NET Framework über Methoden zum Konvertieren eines Pfads (z. B. "C:\whatever.txt") in einen Datei-URI (z. B. "file:///C:/whatever.txt")?

Die System.Uri- Klasse hat das Gegenteil (von einem Datei-URI zu einem absoluten Pfad), aber nichts, was ich für die Konvertierung in einen Datei-URI finden kann.

Dies ist auch keine ASP.NET-Anwendung.

Tinister
quelle

Antworten:

291

Der System.UriKonstruktor kann vollständige Dateipfade analysieren und in URI-Pfade umwandeln. Sie können also einfach Folgendes tun:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
JaredPar
quelle
78
var path = new Uri("file:///C:/whatever.txt").LocalPath;verwandelt einen Uri wieder in einen lokalen Dateipfad für alle, die dies benötigen.
Pondidum
2
Als Hinweis. Diese Art von Uri kann in der VS-Ausgabe und in der Ausgabe von R # -Einheitentests unter Sitzungsfenstern
angeklickt werden
7
Das ist leider nicht richtig. Zum Beispiel new Uri(@"C:\%51.txt").AbsoluteUrigibt Ihnen "file:///C:/Q.txt"statt"file:///C:/%2551.txt"
poizan42
2
Dies funktioniert nicht mit Pfad mit Leerzeichen, dh: "C: \
Testordner
Dies funktioniert auch nicht mit Pfaden, die ein # -Zeichen enthalten.
Lewis
41

Was niemand zu bemerken scheint, ist, dass keiner der System.UriKonstruktoren bestimmte Pfade mit Prozentzeichen korrekt behandelt.

new Uri(@"C:\%51.txt").AbsoluteUri;

Dies gibt Ihnen "file:///C:/Q.txt"statt "file:///C:/%2551.txt".

Keiner der Werte des veralteten dontEscape-Arguments macht einen Unterschied, und die Angabe von UriKind führt ebenfalls zum gleichen Ergebnis. Der Versuch mit dem UriBuilder hilft auch nicht:

new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri

Dies kehrt "file:///C:/Q.txt"ebenfalls zurück.

Soweit ich das beurteilen kann, fehlt dem Framework tatsächlich eine Möglichkeit, dies richtig zu machen.

Wir können es versuchen, indem wir die umgekehrten Schrägstriche durch vordere Schrägstriche ersetzen und den Pfad zu Uri.EscapeUriString- dh vorgeben

new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri

Dies scheint auf den ersten zu arbeiten, aber wenn man es den Weg geben C:\a b.txtdann Sie am Ende mit file:///C:/a%2520b.txtstatt file:///C:/a%20b.txt- irgendwie entscheidet es , dass einige Sequenzen decodiert werden sollten , andere aber nicht. Jetzt könnten wir uns nur noch ein Präfix "file:///"machen, dies \\remote\share\foo.txtberücksichtigt jedoch nicht die UNC-Pfade wie - was unter Windows allgemein akzeptiert zu sein scheint, ist, sie in Pseudo-URLs des Formulars umzuwandeln file://remote/share/foo.txt, daher sollten wir dies ebenfalls berücksichtigen.

EscapeUriStringhat auch das problem, dass es dem '#'charakter nicht entgeht . An diesem Punkt scheinen wir keine andere Wahl zu haben, als unsere eigene Methode von Grund auf neu zu entwickeln. Das schlage ich also vor:

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();
}

Dies lässt absichtlich + und: unverschlüsselt, da dies unter Windows normalerweise so zu sein scheint. Es codiert auch nur latin1, da Internet Explorer Unicode-Zeichen in Datei-URLs nicht verstehen kann, wenn sie codiert sind.

poizan42
quelle
Gibt es ein Nuget, das dies mit einer liberalen Lizenz beinhaltet? Es ist schade, dass es im Framework keinen richtigen Weg dafür gibt und es auch schwierig ist, Copypasta auf dem neuesten Stand zu halten…
Binki
4
Sie können den obigen Code unter den Bedingungen der MIT-Lizenz verwenden (ich glaube nicht, dass etwas so
Kurzes
8

VB.NET:

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

Verschiedene Ausgänge:

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

Einzeiler:

New Uri("D:\Development\~AppFolder\Att\1.gif").AbsoluteUri

Ausgabe :file:///D:/Development/~AppFolder/Att/1.gif

MrCalvin
quelle
2
AbsoluteUriist richtig, weil es auch Leerzeichen in% 20 codiert.
Psulek
Ich bin überzeugt, dass dies unter den gleichen Problemen leidet, die in der Antwort beschrieben wurden, in der es um die Behandlung von Sonderzeichen geht .
Binki
8

Die oben genannten Lösungen funktionieren unter Linux nicht.

Der Versuch, mit .NET Core auszuführen, new Uri("/home/foo/README.md")führt zu einer Ausnahme:

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

Sie müssen der CLR einige Hinweise geben, welche Art von URL Sie haben.

Das funktioniert:

Uri fileUri = new Uri(new Uri("file://"), "home/foo/README.md");

... und die von zurückgegebene Zeichenfolge fileUri.ToString()ist"file:///home/foo/README.md"

Dies funktioniert auch unter Windows.

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

... emittiert "file:///C:/Users/foo/README.md"

Bob Stine
quelle
Wenn Sie wissen, dass der Pfad absolut ist, können Sienew Uri("/path/to/file", UriKind.Absolute);
Minijack
4

Zumindest in .NET 4.5+ können Sie außerdem Folgendes tun:

var uri = new System.Uri("C:\\foo", UriKind.Absolute);
Gavin Greenwalt
quelle
1
Riskieren Sie nicht UriFormatExceptioneinen Tag?
berezovskyi
Dies funktioniert auch nicht richtig, new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUrikehrt "file:///C:/Q.txt"statt"file:///C:/%2551.txt"
poizan42
1

UrlCreateFromPath zur Rettung! Nun, nicht ganz, da es keine erweiterten und UNC-Pfadformate unterstützt, aber das ist nicht so schwer zu überwinden:

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);

Wenn der Pfad mit einem speziellen Präfix beginnt, wird er entfernt. Obwohl die Dokumentation dies nicht erwähnt, gibt die Funktion die Länge der URL aus, auch wenn der Puffer kleiner ist. Daher erhalte ich zuerst die Länge und ordne dann den Puffer zu.

Eine sehr interessante Beobachtung, die ich hatte, ist, dass während "\\ Gerät \ Pfad" korrekt in "Datei: // Gerät / Pfad" transformiert wird, speziell "\\ localhost \ Pfad" nur in "Datei: /// Pfad" transformiert wird. .

Die WinApi-Funktion hat es geschafft, Sonderzeichen zu codieren, lässt jedoch Unicode-spezifische Zeichen im Gegensatz zum Uri- Construtor nicht codiert. In diesem Fall enthält AbsoluteUri die ordnungsgemäß codierte URL, während OriginalString zum Beibehalten der Unicode-Zeichen verwendet werden kann.

IllidanS4 will Monica zurück
quelle
0

Die Problemumgehung ist einfach. Verwenden Sie einfach die Uri (). ToString () -Methode und codieren Sie anschließend Leerzeichen in Prozent, falls vorhanden.

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

Gibt die Datei ordnungsgemäß zurück : /// C: / my% 20example ㄓ .txt

Dinge passieren
quelle