Convertire il percorso del file in un URI di file?

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

La class 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.

Il costruttore System.Uri 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; 

Ciò che nessuno sembra realizzare è che nessuno dei costruttori System.Uri 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" .

Nessuno dei due valori dell’argomento depredato di dontEscape fa alcuna differenza, e specificando UriKind si ottiene lo stesso risultato. Provare con UriBuilder non aiuta neanche:

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

Ciò restituisce "file:///C:/Q.txt" pure.

Per quanto posso dire che il framework è in realtà privo di qualsiasi modo di farlo correttamente.

Possiamo provarci sostituendo i backslash con le barre in avanti e dare il percorso a Uri.EscapeUriString – cioè

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

All’inizio sembra funzionare, ma se si file:///C:/a%2520b.txt il percorso C:\a b.txt si finisce con file:///C:/a%2520b.txt anziché file:///C:/a%20b.txt – in qualche modo decide che alcune sequenze dovrebbero essere decodificate ma non altre. Ora potremmo semplicemente prefissi con "file:///" soli, tuttavia questo non riesce a prendere in considerazione percorsi UNC come \\remote\share\foo.txt – ciò che sembra essere generalmente accettato su Windows è trasformarli in pseudo- urls del file://remote/share/foo.txt modulo file://remote/share/foo.txt , quindi dovremmo tenerne conto.

EscapeUriString ha anche il problema che non sfugge al carattere '#' . 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 in Windows. Inoltre, codifica solo latin1, in quanto Internet Explorer non è in grado di comprendere i caratteri Unicode negli URL dei file se sono codificati.

VB.NET:

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

Uscite diverse:

 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 

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

Le soluzioni di cui sopra non funzionano su Linux.

Utilizzando .NET Core, il tentativo di eseguire un new Uri("/home/foo/README.md") provoca 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 dare 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"

Almeno in .NET 4.5+ puoi anche fare:

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

UrlCreateFromPath in soccorso! Beh, non del tutto, in quanto non supporta i formati di percorsi 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 inizia con un prefisso speciale, viene rimosso. Sebbene la documentazione non lo menzioni, la funzione restituisce la lunghezza dell’URL anche se il buffer è più piccolo, quindi prima ottengo la lunghezza e poi alloco il buffer.

Qualche osservazione molto interessante che ho avuto mentre “\\ device \ path” è stato correttamente trasformato in “file: // device / path”, in particolare “\\ localhost \ path” viene trasformato in “file: /// path” .

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