2009-10-09 7 views
157

.NET Framework có bất kỳ phương pháp nào để chuyển đổi đường dẫn (ví dụ: "C:\whatever.txt") vào URI tệp (ví dụ: "file:///C:/whatever.txt") không?Chuyển đổi đường dẫn tệp thành URI tệp?

Lớp System.Uri có ngược lại (từ URI tệp thành đường dẫn tuyệt đối), nhưng không có gì xa như tôi có thể tìm để chuyển đổi thành URI tệp.

Ngoài ra, đây là không một ứng dụng ASP.NET.

Trả lời

232

Nhà xây dựng System.Uri có khả năng phân tích đường dẫn tệp đầy đủ và biến chúng thành đường dẫn kiểu URI. Vì vậy, bạn chỉ có thể làm như sau:

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

Và tệp đó in: /// c:/foo phải không? – knocte

+0

Ý tôi là, nếu "chuyển đổi" được in ... – knocte

+62

'var path = new Uri (" file: /// C: /whatever.txt ") .LocalPath;' biến Uri trở lại thành một tệp địa phương quá cho bất kỳ ai cần điều này. – Pondidum

7

VB.NET:

Dim URI As New Uri("D:\Development\~AppFolder\Att\1.gif") 

đầu ra khác nhau:

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 

Một lót:

New Uri("D:\Development\~AppFolder\Att\1.gif").AbsoluteUri 

Output: file:///D:/Development/~AppFolder/Att/1.gif

+2

'AbsoluteUri' là đúng vì nó mã hóa khoảng trống thành% 20. – psulek

+0

Tôi đã thuyết phục rằng điều này gặp phải các vấn đề tương tự được mô tả trong [câu trả lời nói về xử lý ký tự đặc biệt] (http://stackoverflow.com/a/35734486/429091). – binki

3

Ít nhất trong .NET 4.5+ bạn cũng có thể làm:

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

Bạn không mạo hiểm nhận được một 'UriFormatException' một ngày? – berezovskyi

+0

Điều này không hoạt động chính xác, hoặc 'mới Uri (@ "C: \% 51.txt", UriKind.Absolute) .AbsoluteUri' trả về '" tệp: /// C: /Q.txt "' thay vì ' "file: /// C: /%2551.txt" ' – poizan42

22

gì không ai dường như nhận ra là không ai trong số các System.Uri constructors xử lý một cách chính xác đường dẫn nhất định với phần trăm dấu hiệu trong đó.

new Uri(@"C:\%51.txt").AbsoluteUri; 

Điều này cung cấp 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 chấp nhận sẽ tạo ra bất kỳ sự khác biệt nào và chỉ định UriKind cũng cho kết quả tương tự. Việc thử với UriBuilder cũng không giúp được:

new UriBuilder() { Scheme = Uri.UriSchemeFile, Host = "", Path = @"C:\%51.txt" }.Uri.AbsoluteUri 

Điều này cũng trả về "file:///C:/Q.txt".

Theo tôi có thể nói khung thực sự thiếu bất kỳ cách nào để thực hiện điều này một cách chính xác.

Chúng tôi có thể thử nó bằng cách thay thế những dấu xồ nguợc với dấu gạch chéo và thức ăn đường dẫn đến Uri.EscapeUriString - tức là

new Uri(Uri.EscapeUriString(filePath.Replace(Path.DirectorySeparatorChar, '/'))).AbsoluteUri 

Điều này dường như làm việc lúc đầu, nhưng nếu bạn cho nó con đường C:\a b.txt sau đó bạn kết thúc với 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 sẽ được giải mã nhưng không được giải mã khác. Bây giờ chúng ta chỉ có thể tiền tố với chính mình là "file:///", tuy nhiên điều này không mang theo các đường dẫn UNC như \\remote\share\foo.txt vào tài khoản - những gì dường như được chấp nhận chung trên Windows là biến chúng thành các url giả dạng file://remote/share/foo.txt, vì vậy chúng ta nên tính đến tốt.

EscapeUriString cũng có vấn đề là nó không thoát khỏi ký tự '#'. Có vẻ như vào thời điểm này, chúng tôi không còn cách nào khác ngoài việc tạo ra phương pháp riêng của mình từ đầu. Vì vậy, đây là những gì tôi đề xuất:

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 + và: không được mã hóa như 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 của tệp nếu chúng được mã hóa.

+0

Có nuget bao gồm điều này với giấy phép tự do không? Thật đáng tiếc là không có cách nào thích hợp cho điều này tồn tại trong khung công tác và việc cập nhật copypasta cũng rất khó khăn… – binki

+1

Bạn có thể sử dụng mã ở trên theo các điều khoản của giấy phép MIT (Tôi không tin rằng điều gì đó ngắn thậm chí có thể có bản quyền, nhưng bây giờ bạn có một khoản trợ cấp rõ ràng) – poizan42

1

Các giải pháp trên không hoạt động trên Linux.

Sử dụng .NET Core, cố gắng thực hiện new Uri("/home/foo/README.md") kết quả trong một ngoại lệ:

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) 
    ... 

Bạn cần phải cung cấp cho các CLR một số gợi ý về những gì sắp xếp của URL mà bạn có.

này hoạt động:

Uri fileUri = new Uri(new Uri("file://"), "home/foo/README.md"); 

... và chuỗi trả về bởi fileUri.ToString()"file:///home/foo/README.md"

này hoạt động trên Windows, quá.

new Uri(new Uri("file://"), @"C:\Users\foo\README.md").ToString()

... phát ra "file:///C:/Users/foo/README.md"

1

UrlCreateFromPath để giải thoát! Vâng, không hoàn toàn, vì nó không hỗ trợ các định dạng con đường mở rộng và UNC, nhưng đó không phải là khó khăn như vậy để vượt qua:

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); 

Trong trường hợp con đường bắt đầu với với một tiền tố đặc biệt, nó được loại bỏ. Mặc dù tài liệu không đề cập đến nó, hàm này sẽ xuất ra độ dài của URL ngay cả khi bộ đệm nhỏ hơn, vì vậy trước hết tôi lấy độ dài và sau đó cấp phát bộ đệm.

Một số rất quan sát thú vị mà tôi có là "\\ device \ path" được chuyển thành "file: // device/path", cụ thể là "\\ localhost \ path" được chuyển thành "tệp :///con đường".

Chức năng WinApi được quản lý để mã hóa các ký tự đặc biệt, nhưng để lại các ký tự Unicode cụ thể chưa được mã hóa, không giống như con số Uri. Trong trường hợp đó, AbsoluteUri chứa URL được mã hóa chính xác, trong khi OriginalString có thể được sử dụng để giữ lại các ký tự Unicode.