Konversi jalur file ke file URI?

201

Apakah .NET Framework memiliki metode apa pun untuk mengubah jalur (mis. "C:\whatever.txt") Menjadi file URI (mis "file:///C:/whatever.txt")?

Kelas System.Uri memiliki kebalikannya (dari file URI ke path absolut), tetapi tidak sejauh yang dapat saya temukan untuk mengonversi ke file URI.

Juga, ini bukan aplikasi ASP.NET.

Tinister
sumber

Jawaban:

292

The System.Urikonstruktor memiliki kemampuan untuk mengurai path file penuh dan mengubahnya menjadi URI gaya jalan. Jadi Anda bisa melakukan hal berikut:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
JaredPar
sumber
78
var path = new Uri("file:///C:/whatever.txt").LocalPath;mengubah Uri kembali menjadi filepath lokal juga untuk siapa saja yang membutuhkan ini.
Pondidum
2
Sebagai catatan. Uri semacam itu dapat diklik dalam output VS dan unit R # menguji output pada sesi windows
AlfeG
7
Sayangnya ini tidak benar. Misalnya new Uri(@"C:\%51.txt").AbsoluteUrimemberi Anda "file:///C:/Q.txt"alih-alih"file:///C:/%2551.txt"
poizan42
2
ini tidak akan bekerja dengan path dengan spasi, yaitu: "C: \ test folder \ whatever.txt"
Quad Coders
Ini juga tidak akan bekerja dengan jalur yang berisi karakter #.
lewis
42

Apa yang tampaknya tidak disadari siapa pun adalah bahwa tidak satu pun System.Urikonstruktor yang menangani dengan benar jalur tertentu dengan tanda persen di dalamnya.

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

Ini memberi Anda "file:///C:/Q.txt"alih-alih "file:///C:/%2551.txt".

Tidak ada nilai dari argumen dontEscape yang ditinggalkan membuat perbedaan, dan menentukan UriKind memberikan hasil yang sama juga. Mencoba dengan UriBuilder juga tidak membantu:

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

Ini juga kembali "file:///C:/Q.txt".

Sejauh yang saya tahu kerangka sebenarnya tidak memiliki cara untuk melakukan ini dengan benar.

Kita dapat mencobanya dengan mengganti garis miring terbalik dengan garis miring ke depan dan memberi makan jalur ke Uri.EscapeUriString- yaitu

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

Ini tampaknya bekerja pada awalnya, tetapi jika Anda memberikan jalan C:\a b.txtmaka Anda berakhir dengan file:///C:/a%2520b.txtbukannya file:///C:/a%20b.txt- entah bagaimana itu memutuskan bahwa beberapa urutan harus diterjemahkan tetapi tidak yang lain. Sekarang kita bisa awalan dengan "file:///"diri kita sendiri, tetapi ini gagal untuk mengambil jalur UNC seperti \\remote\share\foo.txtmemperhitungkan - apa yang tampaknya diterima secara umum pada Windows adalah mengubahnya menjadi uraian semu formulir file://remote/share/foo.txt, jadi kita juga harus memperhitungkannya.

EscapeUriStringjuga memiliki masalah yang tidak luput dari '#'karakter. Tampaknya pada titik ini kita tidak punya pilihan lain selain membuat metode sendiri dari awal. Jadi ini yang saya sarankan:

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

Ini sengaja menyisakan + dan: tidak ter-enkripsi seperti yang biasanya dilakukan pada Windows. Itu juga hanya mengkodekan latin1 karena Internet Explorer tidak dapat memahami karakter unicode dalam url file jika mereka dikodekan.

poizan42
sumber
Apakah ada nuget yang memasukkan ini dengan lisensi liberal? Sayang sekali tidak ada cara yang tepat untuk ini ada dalam kerangka kerja dan menjaga copypasta diperbarui juga sulit ...
binki
4
Anda dapat menggunakan kode di atas di bawah ketentuan lisensi MIT (Saya tidak percaya sesuatu yang singkat bahkan harus memiliki hak cipta, tetapi sekarang Anda memiliki hibah eksplisit)
poizan42
9

Solusi di atas tidak berfungsi di Linux.

Menggunakan .NET Core, berusaha untuk mengeksekusi new Uri("/home/foo/README.md")hasil dalam pengecualian:

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

Anda perlu memberi CLR beberapa petunjuk tentang jenis URL apa yang Anda miliki.

Ini bekerja:

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

... dan string yang dikembalikan oleh fileUri.ToString()adalah"file:///home/foo/README.md"

Ini juga berfungsi pada Windows.

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

... memancarkan "file:///C:/Users/foo/README.md"

Bob Stine
sumber
Jika Anda tahu jalannya absolut, Anda dapat menggunakannew Uri("/path/to/file", UriKind.Absolute);
Minijack
8

VB.NET:

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

Keluaran yang berbeda:

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

Satu liner:

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

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

Tuan Calvin
sumber
2
AbsoluteUriadalah yang benar karena ia mengkodekan juga spasi ke% 20.
psulek
Saya yakin ini mengalami masalah yang sama seperti yang dijelaskan dalam jawaban yang berbicara tentang penanganan karakter khusus .
binki
4

Setidaknya dalam .NET 4.5+ Anda juga dapat melakukan:

var uri = new System.Uri("C:\\foo", UriKind.Absolute);
Gavin Greenwalt
sumber
1
Tidakkah Anda berisiko mendapat UriFormatExceptionsatu hari?
berezovskyi
Ini juga tidak bekerja dengan benar, new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUrikembali "file:///C:/Q.txt"sebagai ganti"file:///C:/%2551.txt"
poizan42
2

UrlCreateFromPath untuk menyelamatkan! Yah, tidak sepenuhnya, karena tidak mendukung format jalur UNC yang diperluas dan, tapi itu tidak terlalu sulit untuk diatasi:

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

Jika path dimulai dengan awalan khusus, itu akan dihapus. Meskipun dokumentasi tidak menyebutkannya, fungsinya menampilkan panjang URL bahkan jika buffer lebih kecil, jadi saya pertama mendapatkan panjangnya dan kemudian mengalokasikan buffer.

Beberapa pengamatan yang sangat menarik yang saya miliki adalah ketika "\\ device \ path" diubah dengan benar menjadi "file: // device / path", khususnya "\\ localhost \ path" ditransformasikan menjadi hanya "file: /// path" .

Fungsi WinApi berhasil menyandikan karakter khusus, tetapi membiarkan karakter khusus Unicode tidak terbaca, tidak seperti konstruktor Uri . Dalam hal ini, AbsoluteUri berisi URL yang disandikan dengan benar, sementara OriginalString dapat digunakan untuk mempertahankan karakter Unicode.

IllidanS4 ingin Monica kembali
sumber
0

Solusinya sederhana. Cukup gunakan metode Uri (). ToString () dan persen-enkode white-space, jika ada, sesudahnya.

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

mengembalikan file dengan benar : /// C: / my% 20example ㄓ .txt

Sesuatu terjadi
sumber