将文件路径转换为文件URI?


201

.NET Framework是否具有将路径(例如"C:\whatever.txt")转换为文件URI(例如"file:///C:/whatever.txt")的任何方法?

System.Uri类具有相反的含义(从文件URI到绝对路径),但据我所知,对于转换为文件URI而言一无所获。

此外,这不是 ASP.NET应用程序。

Answers:


291

System.Uri构造函数解析完整的文件路径,并把它们变成URI风格的路径的能力。因此,您可以执行以下操作:

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

78
var path = new Uri("file:///C:/whatever.txt").LocalPath;对于需要此功能的任何人,也可以将Uri转换回本地文件路径。
Pondidum 2012年

2
作为说明。这类Uri在VS输出和R#单元测试在会话窗口中的输出中都是可单击的
AlfeG

7
不幸的是,这是不正确的。例如,new Uri(@"C:\%51.txt").AbsoluteUri给您"file:///C:/Q.txt"而不是"file:///C:/%2551.txt"
poizan42 '16

2
这不适用于带有空格的路径,即:“ C:\ test folder \ whatever.txt”
Quad Coders

这也不适用于包含#字符的路径。
刘易斯

42

似乎没有人意识到,没有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:///"自己加上前缀,但是这并没有考虑到UNC路径\\remote\share\foo.txt-Windows似乎通常接受的是将它们转换为形式的伪URL file://remote/share/foo.txt,因此我们也应该考虑到这一点。

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字符(如果已编码)。


是否有包含免费许可证的小部件?遗憾的是,在框架中没有适当的方法,并且很难保持copypasta的更新……
binki 2016年

4
您可以根据MIT许可的条款使用上面的代码(我认为简短的内容也不应该具有版权,但是现在您有明确的授权)
poizan42 '16

9

上面的解决方案在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"


如果您知道路径是绝对路径,则可以使用new Uri("/path/to/file", UriKind.Absolute);
Minijack

8

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


2
AbsoluteUri是正确的,因为它也将空格编码为%20。
psulek

我坚信这会遇到与特殊字符处理有关的答案中描述的相同问题。
宾基

4

至少在.NET 4.5+中,您还可以执行以下操作:

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

1
你不冒险有UriFormatException一天吗?
berezovskyi 2015年

这也无法正常工作,而是new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUri返回"file:///C:/Q.txt""file:///C:/%2551.txt"
poizan42 '16

2

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字符。


0

解决方法很简单。只需使用Uri()。ToString()方法,然后对百分比进行空格编码(如果有的话)。

string path = new Uri("C:\my exampleㄓ.txt").ToString().Replace(" ", "%20");

正确返回file:/// C:/ my%20exampleㄓ.txt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.