Convertire il percorso del file in un URI di file?


201

.NET Framework ha dei metodi per convertire un percorso (ad es. "C:\whatever.txt") In un URI di file (ad es. "file:///C:/whatever.txt")?

La classe System.Uri ha il contrario (da un URI di file al percorso assoluto), ma nulla per quanto posso trovare per la conversione in un URI di file.

Inoltre, questa non è un'applicazione ASP.NET.

Risposte:


292

Il System.Uricostruttore ha la capacità di analizzare percorsi di file completi e trasformarli in percorsi di stile URI. Quindi puoi semplicemente fare quanto segue:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;

78
var path = new Uri("file:///C:/whatever.txt").LocalPath;trasforma un Uri in un percorso file locale anche per chiunque ne abbia bisogno.
Pondidum,

2
Come una nota. Questo tipo di Uri è selezionabile nell'output VS e nell'output dei test unitari R # nelle finestre della sessione
AlfeG,

7
Questo purtroppo non è corretto. Ad esempio new Uri(@"C:\%51.txt").AbsoluteUriti dà "file:///C:/Q.txt"invece di"file:///C:/%2551.txt"
poizan42

2
questo non funzionerà con il percorso con spazi, ad es .: "C: \ test folder \ whatever.txt"
Quad Coders

Né funzionerà con percorsi che contengono un carattere #.
Lewis,

42

Ciò che nessuno sembra rendersi conto è che nessuno dei System.Uricostruttori gestisce correttamente determinati percorsi con segni di percentuale in essi.

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

Questo ti dà "file:///C:/Q.txt"invece di "file:///C:/%2551.txt".

Né i valori dell'argomento deprecato dontEscape fanno alcuna differenza, e specificando UriKind si ottiene anche lo stesso risultato. Provare con UriBuilder non aiuta neanche:

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

Anche questo ritorna "file:///C:/Q.txt".

Per quanto ne so, il framework in realtà non ha alcun modo di farlo correttamente.

Possiamo provarci sostituendo le barre rovesciate con barre in avanti e alimentare il percorso verso Uri.EscapeUriString- ad es

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

Questo sembra funzionare all'inizio, ma se gli dai il percorso C:\a b.txtallora finisci file:///C:/a%2520b.txtinvece di file:///C:/a%20b.txt- in qualche modo decide che alcune sequenze dovrebbero essere decodificate ma non altre. Ora potremmo prefissarci solo con "file:///"noi stessi, tuttavia questo non tiene \\remote\share\foo.txtconto dei percorsi UNC come quello che sembra essere generalmente accettato su Windows è trasformarli in pseudo-url del modulo file://remote/share/foo.txt, quindi dovremmo tenerne conto anche.

EscapeUriStringha anche il problema che non sfugge al '#'personaggio. A questo punto sembrerebbe che non abbiamo altra scelta che fare il nostro metodo da zero. Quindi questo è quello che suggerisco:

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

Questo lascia intenzionalmente + e: non codificato in quanto sembra essere come di solito viene fatto su Windows. Codifica anche latin1 poiché Internet Explorer non è in grado di comprendere i caratteri unicode negli URL dei file se sono codificati.


Esiste un nuget che include questo con una licenza liberale? È un peccato che non esista un modo adeguato per farlo nel quadro e anche mantenere copypasta aggiornata è difficile ...
binki,

4
È possibile utilizzare il codice sopra riportato sotto i termini della licenza del MIT (non credo che qualcosa di così breve debba anche essere protetto da copyright, ma ora hai una concessione esplicita)
poizan42

9

Le soluzioni sopra non funzionano su Linux.

Utilizzando .NET Core, tentando di eseguire i new Uri("/home/foo/README.md")risultati in un'eccezione:

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

Devi fornire al CLR alcuni suggerimenti su quale tipo di URL hai.

Questo funziona:

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

... e la stringa restituita da fileUri.ToString()è"file:///home/foo/README.md"

Funziona anche su Windows.

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

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


Se sai che il percorso è assoluto puoi usarenew Uri("/path/to/file", UriKind.Absolute);
Minijack

8

VB.NET:

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

Diverse uscite:

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

Una fodera:

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

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


2
AbsoluteUriè corretto perché codifica anche gli spazi su% 20.
psulek,

Sono convinto che questo soffra degli stessi problemi descritti nella risposta che parla della gestione speciale del personaggio .
binki,

4

Almeno in .NET 4.5+ puoi anche fare:

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

1
Non rischi di ottenere un UriFormatExceptiongiorno?
berezovskyi,

Anche questo non funziona correttamente, new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUrirestituisce "file:///C:/Q.txt"invece di"file:///C:/%2551.txt"
poizan42

2

UrlCreateFromPath in soccorso! Bene, non del tutto, poiché non supporta formati di percorso estesi e UNC, ma non è così difficile da superare:

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

Nel caso in cui il percorso inizi con un prefisso speciale, viene rimosso. Anche se la documentazione non lo menziona, la funzione genera la lunghezza dell'URL anche se il buffer è più piccolo, quindi ottengo prima la lunghezza e quindi allocare il buffer.

Alcuni osservazione molto interessante che ho avuto è che mentre "\\ dispositivo \ percorso" viene correttamente trasformato in "file: // dispositivo / percorso", in particolare "\\ localhost \ percorso" viene trasformato in solo "file: /// percorso" .

La funzione WinApi è riuscita a codificare caratteri speciali, ma lascia i caratteri specifici di Unicode non codificati, a differenza del costruttore Uri . In tal caso, AbsoluteUri contiene l'URL correttamente codificato, mentre OriginalString può essere utilizzato per conservare i caratteri Unicode.


0

La soluzione è semplice. Basta usare il metodo Uri (). ToString () e successivamente codificare gli spazi bianchi, se presenti.

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

restituisce correttamente il file: /// C: / my% 20example ㄓ .txt

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.