.NET Framework是否有任何方法将路径(例如"C:\whatever.txt"
)转换为文件URI(例如"file:///C:/whatever.txt"
)?
System.Uri类具有相反的(从文件URI到绝对路径),但我没有找到转换为文件URI的任何内容。
此外,这不是ASP.NET应用程序。
System.Uri
构造函数能够解析完整的文件路径并将它们转换为URI样式路径。所以你可以做到以下几点:
var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
似乎没有人意识到,没有任何一个System.Uri
构造函数能正确处理某些带有百分号的路径。
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"
。
据我所知,框架实际上缺乏正确的方法。
我们可以尝试用正斜杠替换反斜杠并将路径输入Uri.EscapeUriString
- 即
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
这似乎首先起作用,但如果你给它路径C:\a b.txt
然后你最终得到file:///C:/a%2520b.txt
而不是file:///C:/a%20b.txt
- 不知何故它决定一些序列应该解码而不是其他序列。现在我们可以自己加上"file:///"
的前缀,但是这没有考虑到像\\remote\share\foo.txt
这样的UNC路径 - 在Windows上似乎普遍接受的是将它们变成file://remote/share/foo.txt
形式的伪URL,所以我们应该考虑到这一点好。
EscapeUriString
也有一个问题,它没有逃脱'#'
角色。在这一点上,我们似乎别无选择,只能从头开始制作我们自己的方法。所以这就是我的建议:
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
一个班轮:
New Uri("D:\Development\~AppFolder\Att\1.gif").AbsoluteUri
输出:
file:///D:/Development/~AppFolder/Att/1.gif
上述解决方案不适用于Linux。
使用.NET Core,尝试执行new Uri("/home/foo/README.md")
会导致异常:
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()
返回的字符串是"file:///home/foo/README.md"
这也适用于Windows。
new Uri(new Uri("file://"), @"C:\Users\foo\README.md").ToString()
......发出"file:///C:/Users/foo/README.md"
至少在.NET 4.5+中你也可以这样做:
var uri = new System.Uri("C:\\foo", UriKind.Absolute);
UrlCreateFromPath救援!好吧,并非完全,因为它不支持扩展和UNC路径格式,但这不是很难克服:
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);
如果路径以特殊前缀开头,则会删除它。虽然文档没有提到它,但即使缓冲区较小,函数也会输出URL的长度,所以我首先获取长度,然后分配缓冲区。
我有一个非常有趣的观察是,当“\\ device \ path”被正确转换为“file:// device / path”时,特别是“\\ localhost \ path”被转换为“file:/// path” 。
WinApi函数设法对特殊字符进行编码,但不像Uri construtor那样保留未编码的Unicode特定字符。在这种情况下,AbsoluteUri包含正确编码的URL,而OriginalString可用于保留Unicode字符。
解决方法很简单。之后只需使用Uri()。ToString()方法和百分比编码空格(如果有的话)。
string path = new Uri("C:\my exampleㄓ.txt").ToString().Replace(" ", "%20");
正确返回file:/// C:/ my%20exampleㄓ.txt