Điều mà không ai có thể nhận ra là không ai trong số các nhà System.Uri
xây dựng xử lý chính xác các đường dẫn nhất định có dấu phần trăm trong đó.
new Uri(@"C:\%51.txt").AbsoluteUri;
Điều này mang lại cho bạn "file:///C:/Q.txt"
thay vì "file:///C:/%2551.txt"
.
Không có giá trị nào của đối số dontEscape không được tạo ra có sự khác biệt nào và việc chỉ định UriKind cũng cho kết quả tương tự. Thử với UriBuilder cũng không giúp được gì:
new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri
Điều này trở lại "file:///C:/Q.txt"
là tốt.
Theo như tôi có thể nói khung thực sự thiếu bất kỳ cách nào để làm điều này một cách chính xác.
Chúng ta có thể thử nó bằng cách thay thế dấu gạch chéo ngược bằng dấu gạch chéo về phía trước và đưa đường dẫn đến Uri.EscapeUriString
- tức là
new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri
Điều này có vẻ hoạt động lúc đầu, nhưng nếu bạn cho nó đường dẫn C:\a b.txt
thì cuối cùng bạn sẽ file:///C:/a%2520b.txt
thay vì file:///C:/a%20b.txt
- bằng cách nào đó nó quyết định rằng một số chuỗi nên được giải mã nhưng không phải là các chuỗi khác. Bây giờ chúng ta chỉ có thể có tiền tố với "file:///"
chính mình, tuy nhiên điều này không tính đến các đường dẫn UNC như \\remote\share\foo.txt
tài khoản - điều dường như thường được chấp nhận trên Windows là biến chúng thành dạng giả file://remote/share/foo.txt
, vì vậy chúng ta cũng nên tính đến điều đó.
EscapeUriString
cũng có vấn đề là nó không thoát khỏi '#'
nhân vật. Tại thời điểm này, dường như chúng ta không có lựa chọn nào khác ngoài việc thực hiện phương pháp của riêng mình từ đầu. Vì vậy, đây là những gì tôi đề nghị:
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();
}
Điều này cố ý để lại dấu + và: không được mã hóa vì đó dường như là cách nó thường được thực hiện trên Windows. Nó cũng chỉ mã hóa latin1 vì Internet Explorer không thể hiểu các ký tự unicode trong url tệp nếu chúng được mã hóa.
var path = new Uri("file:///C:/whatever.txt").LocalPath;
biến một Uri trở lại thành một filepath địa phương cho bất cứ ai cần điều này.