แปลงพา ธ ไฟล์เป็นไฟล์ URI หรือไม่


201

. NET Framework มีวิธีการใด ๆ ในการแปลงเส้นทาง (เช่น"C:\whatever.txt") ไปเป็นไฟล์ URI (เช่น"file:///C:/whatever.txt") หรือไม่?

System.Uriชั้นจะมีย้อนกลับ (จากแฟ้ม URI ไปยังเส้นทางที่แน่นอน) แต่ไม่มีอะไรเท่าที่ผมสามารถหาสำหรับการแปลงเป็นไฟล์ URI

นอกจากนี้นี่ไม่ใช่แอปพลิเคชัน ASP.NET

คำตอบ:


292

ตัวSystem.Uriสร้างมีความสามารถในการแยกวิเคราะห์พา ธ ไฟล์แบบเต็มและเปลี่ยนให้เป็นพา ธ สไตล์ URI ดังนั้นคุณสามารถทำสิ่งต่อไปนี้:

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

78
var path = new Uri("file:///C:/whatever.txt").LocalPath;เปลี่ยน Uri กลับไปเป็น filepath ในระบบด้วยสำหรับทุกคนที่ต้องการสิ่งนี้
Pondidum

2
เป็นบันทึก Uri ประเภทนั้นสามารถคลิกได้ในเอาท์พุท VS และ R # หน่วยทดสอบเอาต์พุตที่หน้าต่างเซสชัน
AlfeG

7
น่าเสียดายที่มันไม่ถูกต้อง ตัวอย่างเช่นnew Uri(@"C:\%51.txt").AbsoluteUriให้คุณ"file:///C:/Q.txt"แทน"file:///C:/%2551.txt"
poizan42

2
นี้จะไม่ทำงานกับเส้นทางที่มีช่องว่างเช่น: "C: \ test folder \ Anything.txt"
Quad Coders

และจะไม่ทำงานกับเส้นทางที่มีอักขระ # ตัว
lewis

42

สิ่งที่ดูเหมือนจะไม่มีใครรู้คือไม่มีสิ่ง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 ไม่สามารถเข้าใจอักขระ unicode ใน URL ของไฟล์หากมีการเข้ารหัส


มีนักเก็ตที่รวมสิ่งนี้ไว้ในใบอนุญาตเสรีหรือไม่? มันน่าเสียดายที่วิธีการที่ไม่ถูกต้องสำหรับสิ่งนี้มีอยู่ในกรอบงานและทำให้การปรับปรุง copypasta นั้นยากเกินไป ...
binki

4
คุณสามารถใช้รหัสข้างต้นภายใต้เงื่อนไขของใบอนุญาต MIT (ฉันไม่เชื่อว่าสิ่งที่สั้นควรเป็นลิขสิทธิ์ แต่ตอนนี้คุณมีสิทธิ์ที่ชัดเจน)
poizan42

9

โซลูชันด้านบนไม่ทำงานบน 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"


หากคุณรู้ว่าเส้นทางนั้นแน่นอนคุณสามารถใช้new Uri("/path/to/file", UriKind.Absolute);
Minijack

8

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

ฉันเชื่อว่าได้รับความทุกข์จากปัญหาเดียวกันที่อธิบายไว้ในคำตอบที่พูดคุยเกี่ยวกับการจัดการอักขระพิเศษ
binki

4

อย่างน้อยใน. NET 4.5+ คุณสามารถทำได้:

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

1
คุณไม่เสี่ยงที่จะได้รับUriFormatExceptionวันเดียวหรือ
berezovskyi

สิ่งนี้ทำงานได้ไม่ถูกต้องnew Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUriส่งคืน"file:///C:/Q.txt"แทนที่จะเป็น"file:///C:/%2551.txt"
poizan42

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 แม้ว่าบัฟเฟอร์จะมีขนาดเล็กลงดังนั้นฉันจะได้รับความยาวก่อนแล้วจึงจัดสรรบัฟเฟอร์

บางมากสังเกตที่น่าสนใจผมมีคือในขณะที่ "อุปกรณ์ \\ \ เส้นทาง" จะถูกเปลี่ยนไปอย่างถูกต้อง "file: // อุปกรณ์ / เส้นทาง" โดยเฉพาะ "\\ localhost \ เส้นทาง" จะเปลี่ยนไปเพียง "แฟ้ม: เส้นทาง ///" .

ฟังก์ชัน WinApi จัดการเพื่อเข้ารหัสอักขระพิเศษ แต่ปล่อยให้อักขระเฉพาะ Unicode ไม่มีการเข้ารหัสซึ่งแตกต่างจากUri construtor ในกรณีนั้นAbsoluteUriมี URL ที่เข้ารหัสอย่างถูกต้องในขณะที่OriginalStringสามารถใช้เพื่อรักษาอักขระ Unicode


0

วิธีแก้ปัญหาง่าย เพียงใช้เมธอด Uri () ToString () และช่องว่างเปอร์เซ็นต์การเข้ารหัสสีขาวถ้ามีหลังจากนั้น

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

ส่งคืนไฟล์อย่างถูกต้อง: /// C: /% 20 ตัวอย่างของฉันㄓ .txt

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.