将文件path转换为文件URI?

.net框架是否有任何方法转换path(例如"C:\whatever.txt" )到一个文件的URI(例如"file:///C:/whatever.txt" )?

System.Uri类有相反的结果(从一个文件的URI到绝对path),但没有什么可以find转换为一个文件的URI。

另外,这不是一个ASP.NET应用程序。

System.Uri构造函数能够parsing完整的文件path并将其转换为URI样式的path。 所以你可以做到以下几点:

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

没有人似乎意识到的是,没有一个System.Uri构造函数正确地处理某些具有百分号的path。

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

这会给你"file:///C:/Q.txt"而不是"file:///C:/%2551.txt"

弃用的dontEscape参数的值都没有任何区别,并且指定UriKind也给出相同的结果。 试用UriBuilder也没有帮助:

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

这也返回"file:///C:/Q.txt"

据我所知,框架实际上缺乏正确的做法。

我们可以尝试通过用正斜杠replace反斜杠并将path提供给Uri.EscapeUriString – 即

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

这似乎起初工作,但如果你给它的pathC:\a b.txt然后你最终与file:///C:/a%2520b.txt而不是file:///C:/a%20b.txt – 不知何故,它决定了一些序列应该被解码,而不是其他的。 现在我们可以自己加上"file:///"前缀,但是却没有把\\remote\share\foo.txt这样的UNCpath考虑进去 – 在Windows上似乎被普遍接受的是把它们变成伪 -表单file://remote/share/foo.txt URL file://remote/share/foo.txt ,所以我们也应该考虑到这一点。

EscapeUriString也有问题,它不会转义'#'字符。 在这一点上,似乎我们别无select,只能从头开始自己的方法。 所以这是我的build议:

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

这故意留下+和:未编码,因为这似乎是通常在Windows上完成的。 它也只编码latin1,因为Internet Explorer不能理解文件URL中的Unicode字符(如果它们是编码的)。

VB.NET:

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

不同的输出:

 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 

一个class轮:

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

输出file:///D:/Development/~AppFolder/Att/1.gif

至less在.NET 4.5 +中,你也可以这样做:

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

上面的解决scheme在Linux上不起作用。

使用.NET Core尝试执行new Uri("/home/foo/README.md")导致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) ... 

你需要给CLR一些关于你的URL的提示。

这工作:

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

…和fileUri.ToString()返回的string是"file:///home/foo/README.md"

这也适用于Windows。

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

…发出"file:///C:/Users/foo/README.md"

UrlCreateFromPath来救援! 好吧,不是完全的,因为它不支持扩展和UNCpath格式,但是这并不难解决:

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

如果path以一个特殊的前缀开头,它将被删除。 虽然文档没有提到它,但是即使缓冲区较小,该函数也会输出URL的长度,所以我首先获取长度然后分配缓冲区。

我有一个非常有趣的观察,就是“\\ device \ path”被正确转换为“file:// device / path”,具体地说“\\ localhost \ path”被转换为“file:/// path” 。

WinApi函数设法对特殊字符进行编码,但与Uri construtor不同,它使Unicode特定的字符不被编码。 在这种情况下, AbsoluteUri包含正确编码的URL,而OriginalString可以用来保留Unicode字符。