2009-10-09 120 views
157

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

System.Uri类有相反的结果(从文件URI到绝对路径),但没有什么可以找到转换为文件URI。

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

回答

232

构造函数System.Uri能够解析完整文件路径并将它们转换为URI样式路径。所以,你可以做到以下几点:

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

这打印文件:/// C:/富吗? – knocte 2012-10-21 02:31:19

+0

我的意思是,如果“转换”打印...... – knocte 2012-10-21 12:29:56

+62

'变种路径=新URI(“文件:/// C:/whatever.txt”).LocalPath;'导通一个URI返回到一个本地文件路径过于为任何人这需要这个。 – Pondidum 2012-11-08 16:06:40

7

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 2015-02-12 10:22:46

+0

我相信,这会遭遇[谈论特殊字符处理的答案](http://stackoverflow.com/a/35734486/429091)中描述的相同问题。 – binki 2016-10-26 20:46:42

3

至少在.NET 4.5 +你也可以这样做:

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

有一天你不会冒险得到'UriFormatException'吗? – berezovskyi 2015-12-03 07:33:12

+0

这也不能正常工作,'新Uri(@“C:\%51.txt”,UriKind.Absolute).AbsoluteUri'返回 '“file:/// C:/Q.txt”'而不是' “file:/// C:/%2551.txt” – poizan42 2016-03-01 20:58:58

22

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

+0

有没有包含这个自由许可证的nuget?遗憾的是没有正确的方法,因为在框架中存在此问题,并且保留副本更新也很困难... – binki 2016-10-26 20:48:10

+1

您可以使用上述MIT代码许可证条款中的代码(我不相信那些简短的内容甚至可以版权保护,但现在你有明确的授权) – poizan42 2016-10-26 23:16:24

1

的解决方案上面的不工作在Linux上。

使用.NET的核心,试图在一个例外执行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"

1

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的长度,所以我首先获取长度,然后分配缓冲区。

一些非常有趣的观察我的是,虽然“\\设备\路径”是否正确转化为“文件://设备/路径”,特别是“\\本地主机\路径”转化为只是“文件:///路径”。

WinApi函数设法对特殊字符进行编码,但留下Unicode编码的特殊字符,不像Uri construtor。在这种情况下,绝对URI包含正确编码的URL,而OriginalString可以用来保留Unicode字符。