228

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

System.Uri类具有相反的效果(从文件 URI 到绝对路径),但据我所知,没有任何东西可以转换为文件 URI。

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

4

7 回答 7

325

构造System.Uri函数能够解析完整的文件路径并将它们转换为 URI 样式路径。因此,您可以执行以下操作:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
于 2009-10-09T22:45:54.390 回答
51

似乎没有人意识到,没有一个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 字符(如果它们被编码)。

于 2016-03-01T22:02:55.370 回答
13

上述解决方案不适用于 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"

于 2017-01-16T14:00:34.327 回答
9

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

于 2014-07-16T22:09:04.183 回答
5

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

var uri = new System.Uri("C:\\foo", UriKind.Absolute);
于 2014-07-22T01:20:22.357 回答
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 函数设法对特殊字符进行编码,但不编码特定于 Unicode 的字符,这与Uri构造函数不同。在这种情况下,AbsoluteUri包含正确编码的 URL,而OriginalString可用于保留 Unicode 字符。

于 2017-09-02T13:37:44.480 回答
-1

解决方法很简单。之后只需使用 Uri().ToString() 方法和百分比编码空格(如果有的话)。

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

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

于 2019-03-28T00:28:09.457 回答