. net框架是否有任何方法来转换路径(例如。"C:\whatever.txt")转换为文件URI(例如:“文件:/ / / C: / whatever.txt”)?
这个系统。Uri类具有相反的情况(从文件Uri到绝对路径),但就我所能找到的转换为文件Uri而言,没有任何东西。
另外,这不是一个ASP。网络应用程序。
. net框架是否有任何方法来转换路径(例如。"C:\whatever.txt")转换为文件URI(例如:“文件:/ / / C: / whatever.txt”)?
这个系统。Uri类具有相反的情况(从文件Uri到绝对路径),但就我所能找到的转换为文件Uri而言,没有任何东西。
另外,这不是一个ASP。网络应用程序。
当前回答
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构造函数那样对特定于unicode的字符进行编码。在这种情况下,AbsoluteUri包含正确编码的URL,而OriginalString可用于保留Unicode字符。
其他回答
似乎没有人意识到的是,没有一个系统。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
This seems to work at first, but if you give it the path C:\a b.txt then you end up with file:///C:/a%2520b.txt instead of file:///C:/a%20b.txt - somehow it decides that some sequences should be decoded but not others. Now we could just prefix with "file:///" ourselves, however this fails to take UNC paths like \\remote\share\foo.txt into account - what seems to be generally accepted on Windows is to turn them into pseudo-urls of the form file://remote/share/foo.txt, so we should take that into account as well.
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字符。
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构造函数那样对特定于unicode的字符进行编码。在这种情况下,AbsoluteUri包含正确编码的URL,而OriginalString可用于保留Unicode字符。
变通办法很简单。只需使用Uri(). tostring()方法,然后使用百分比编码的空格(如果有的话)。
string path = new Uri("C:\my exampleㄓ.txt").ToString().Replace(" ", "%20");
正确返回文件:///C:/my%20exampleㄓ.txt
Unfortunately @poizan42 answer does not take into account the fact that we live in a Unicode world and it's too restrictive according to RFC3986. The accepted answer of @pierre-arnaud and @jaredpar relies on the System.Uri constructor that has to take care for too many components of the Uri to be able to manage the variability of file names and it fails poorly on percent character and others cases. The other answers are simplicistics or simply unuseful. The best one would have been @is4, but after posting the first version of this post I tested it together in the test case I wrote for mine and it fails on many Unicode characters.
在我的例子中,我开始研究@poizan42代码和各种答案注释什么是有效的,什么是无效的,所以我采取了稍微不同的方法。
首先,我认为输入字符串是一个有效的文件路径,所以我在测试中使用所有有效的unicode字符和代理对以编程方式创建了路径。有了这个,我验证了至少Path.GetInvalidFileNameChars()似乎至少在Windows中返回正确的集。 然后,我将这些路径传递给一个方法,该方法是根据路径的ABNF规则实现的,您可以在https://www.ietf.org/rfc/rfc3986.txt的第22页中找到该规则。
我比较了它的结果与什么UriBuilder生成,这是结果修复:
public static string FilePathToFileUrl(string path)
{
return new UriBuilder("file",string.Empty)
{
Path = path
.Replace("%",$"%{(int)'%':X2}")
.Replace("[",$"%{(int)'[':X2}")
.Replace("]",$"%{(int)']':X2}"),
}
.Uri
.AbsoluteUri;
}
这是完全未优化的,并执行三次替换,所以请随意将其转换为Span或StringBuilder。
至少在。net 4.5+中你还可以做到:
var uri = new System.Uri("C:\\foo", UriKind.Absolute);