将文件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字符。