Chuyển đổi đường dẫn tệp thành URI tệp?


201

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

Lớp System.Uri có đảo ngược (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 thấy để chuyển đổi sang URI tệp.

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

Câu trả lời:


292

Hàm System.Uritạo có khả năng phân tích các đường dẫn tệp đầy đủ và biến chúng thành các đườ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;

78
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.
Pondidum

2
Như một lưu ý. Những loại Uri này có thể nhấp được trong đầu ra VS và đầu ra đơn vị R # kiểm tra tại các cửa sổ phiên
AlfeG

7
Thật không may là không chính xác. Ví dụ, new Uri(@"C:\%51.txt").AbsoluteUricung cấp cho bạn "file:///C:/Q.txt"thay vì"file:///C:/%2551.txt"
poizan42

2
điều này sẽ không hoạt động với đường dẫn có dấu cách, tức là: "C: \ test thư mục \ anything.txt"
Quad Coders

Điều này cũng sẽ không hoạt động với các đường dẫn có chứa ký tự #.
lewis

42

Điều mà không ai có thể nhận ra là không ai trong số các nhà System.Urixâ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.txtthì cuối cùng bạn sẽ file:///C:/a%2520b.txtthay 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.txttà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 đó.

EscapeUriStringcũ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.


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

4
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 thứ 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

9

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 cung cấp cho CLR một số gợi ý về loại URL bạn có.

Những công việc này:

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

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

Điều này cũng hoạt động trên Windows.

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

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


Nếu bạn biết đường dẫn là tuyệt đối bạn có thể sử dụngnew Uri("/path/to/file", UriKind.Absolute);
Minijack

8

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

Lót:

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

Đầu ra :file:///D:/Development/~AppFolder/Att/1.gif


2
AbsoluteUrilà chính xác bởi vì nó cũng mã hóa khoảng trắng thành% 20.
psulek

Tôi tin rằng điều này chịu đựng những vấn đề tương tự được mô tả trong câu trả lời nói về việc xử lý nhân vật đặc biệt .
binki

4

Í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 có nguy cơ nhận được một UriFormatExceptionngày?
berezovskyi

Điều này cũng không hoạt động chính xác, new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUritrả về "file:///C:/Q.txt"thay vì"file:///C:/%2551.txt"
poizan42

2

UrlCreateFromPath để giải cứu! Chà, không hoàn toàn, vì nó không hỗ trợ các định dạng đường dẫn mở rộng và UNC, nhưng điều đó không quá khó để 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 đường dẫn bắt đầu bằng một tiền tố đặc biệt, nó sẽ bị xóa. Mặc dù tài liệu không đề cập đến nó, nhưng hàm xuất ra độ dài của URL ngay cả khi bộ đệm nhỏ hơn, vì vậy trước tiên tôi lấy độ dài và sau đó phân bổ bộ đệm.

Một quan sát rất thú vị mà tôi có là trong khi "\\ device \ path" được chuyển đổi chính xác thành "file: // device / path", cụ thể là "\\ localhost \ path" được chuyển thành "file: /// path" .

Hàm WinApi được quản lý để mã hóa các ký tự đặc biệt, nhưng không để các ký tự cụ thể Unicode không được mã hóa, không giống như hàm con 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.


0

Cách giải quyết rất đơn giản. Chỉ cần sử dụng phương thức Uri (). ToString () và mã hóa phần trăm khoảng trắng, nếu có, sau đó.

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

trả về đúng tệp: /// C: / my% 20example ㄓ .txt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.