Dosya yolu bir dosya URI'sine dönüştürülsün mü?


204

.NET Framework'te yolu (ör. "C:\whatever.txt") Bir dosya URI'sine (ör. "file:///C:/whatever.txt") Dönüştürmek için herhangi bir yöntem var mı ?

System.Uri sınıf (mutlak yol bir dosyanın URI itibaren) ters vardır, ama hiçbir şey bildiğim kadarıyla bir dosyaya URI'siyle dönüştürmek için bulabilirsiniz olarak.

Ayrıca, bu bir ASP.NET uygulaması değil .

Yanıtlar:


294

System.UriYapıcı tam dosya yollarını ayrıştırmak ve URI'nın tarzı yolları haline getirmek yeteneği vardır. Böylece aşağıdakileri yapabilirsiniz:

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

78
var path = new Uri("file:///C:/whatever.txt").LocalPath;buna ihtiyacı olan herkes için Uri'yi tekrar yerel bir dosya yoluna dönüştürür.
Pondidum

2
Not olarak. Bu tür Uri, VS çıktısında tıklanabilir ve oturum pencerelerinde R # birimi testleri çıktısı
AlfeG

7
Bu maalesef doğru değil. Örneğin new Uri(@"C:\%51.txt").AbsoluteUriverir "file:///C:/Q.txt"yerine"file:///C:/%2551.txt"
poizan42

2
bu, boşluklu yolla çalışmaz, yani: "C: \ test folder \ whatever.txt"
Quad Coders

Bu, # karakteri içeren yollarla da çalışmaz.
lewis

42

Kimsenin farkına varmadığı şey, System.Uriinşaatçıların hiçbirinin belirli yolları, içinde yüzde işaretleri olan doğru şekilde işlememesi.

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

Bu size "file:///C:/Q.txt"bunun yerine verir "file:///C:/%2551.txt".

Kullanımdan kaldırılan dontEscape bağımsız değişkeninin değerleri de herhangi bir fark yaratmaz ve UriKind'in belirtilmesi de aynı sonucu verir. UriBuilder ile denemek de yardımcı olmaz:

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

Bu da geri döner "file:///C:/Q.txt".

Çerçevenin aslında bunu doğru şekilde yapmanın herhangi bir yolu olmadığını söyleyebildiğim kadarıyla.

Ters eğik çizgileri ileri eğik çizgilerle değiştirerek ve Uri.EscapeUriString-

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

Bu başlangıçta işe yarıyor gibi görünüyor, ancak yolu C:\a b.txtverirseniz, file:///C:/a%2520b.txtbunun yerine sonuçlanırsınız file:///C:/a%20b.txt- bir şekilde bazı dizilerin kodunun çözülmesine karar verir, ancak diğerleri değil. Şimdi "file:///"kendimizle ön ek yapabiliriz , ancak bu UNC yollarını \\remote\share\foo.txthesaba katmaz - Windows'da genel olarak kabul edilen şey, onları formun sahte url'lerine dönüştürmektir file://remote/share/foo.txt, bu yüzden bunu da dikkate almalıyız.

EscapeUriString'#'karakterden kaçma problemi de var . Bu noktada, kendi yöntemimizi sıfırdan yapmaktan başka seçeneğimiz yok gibi görünüyor. Yani önerdiğim bu:

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

Bu bilerek + ve: kodlarını, genellikle Windows'taki gibi göründüğü gibi kodlanmamış bırakır. Internet Explorer, kodlanmışlarsa dosya url'lerindeki unicode karakterleri anlayamadığından yalnızca latin1 kodlar.


Bunu liberal bir lisansla içeren bir külçe var mı? Yazık bu çerçeve için uygun bir yolu yoktur ve copypasta güncel tutmak da zor ...
Binki

4
Yukarıdaki kodu MIT lisansı koşulları altında kullanabilirsiniz (Kısa bir şeyin telif hakkına sahip olması gerektiğine inanmıyorum, ancak şimdi açık bir hibeniz var)
poizan42

10

Yukarıdaki çözümler Linux üzerinde çalışmaz.

.NET Core'u kullanarak new Uri("/home/foo/README.md"), bir istisnada sonuçları yürütmeye çalışmak :

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'ye ne tür bir URL'niz olduğuna dair bazı ipuçları vermeniz gerekir.

Bu çalışıyor:

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

... ve dize tarafından döndürülen fileUri.ToString()DİR"file:///home/foo/README.md"

Bu Windows üzerinde de çalışır.

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

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


Yolun mutlak olduğunu biliyorsanız kullanabilirsiniznew Uri("/path/to/file", UriKind.Absolute);
Minijack

8

VB.NET:

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

Farklı çıktılar:

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

Bir astar:

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

Çıktı :file:///D:/Development/~AppFolder/Att/1.gif


2
AbsoluteUridoğru olanıdır, çünkü boşlukları% 20 olarak kodlar.
psulek

Bunun , özel karakter işleme hakkında konuşan cevapta açıklanan sorunlardan muzdarip olduğuna inanıyorum .
binki

4

En azından .NET 4.5+ sürümünde ayrıca şunları da yapabilirsiniz:

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

1
UriFormatExceptionBir gün alma riskiniz yok mu?
berezovskyi

Bu da düzgün çalışmıyor, bunun yerine new Uri(@"C:\%51.txt",UriKind.Absolute).AbsoluteUrigeri dönüyor"file:///C:/Q.txt""file:///C:/%2551.txt"
poizan42

2

UrlCreateFromPath kurtarmaya! Tamamen değil, genişletilmiş ve UNC yol biçimlerini desteklemediğinden, üstesinden gelmek o kadar da zor değil:

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

Yolun özel bir önekle başlaması durumunda kaldırılır. Belgeler bundan bahsetmese de, arabellek daha küçük olsa bile işlev URL'nin uzunluğunu verir, bu yüzden önce uzunluğu elde ederim ve sonra arabelleği ayırırım.

Bazı çok ilginç gözlem "\\ aygıt \ yol" doğru "dosya: // aygıt / yol", özellikle "\\ localhost \ yol" sadece "dosya: /// yol" dönüştürülür olmasıdır .

WinApi işlevi özel karakterleri kodlamayı başardı, ancak Uri sıkıştırıcısının aksine Unicode'a özgü karakterleri kodlanmamış bırakır . Bu durumda, AbsoluteUri düzgün şekilde kodlanmış URL'yi içerirken, OriginalString Unicode karakterlerini korumak için kullanılabilir.


0

Çözüm basittir. Sadece daha sonra Uri (). ToString () yöntemini ve varsa beyaz boşlukların yüzde kodlamasını kullanın.

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

dosyayı düzgün döndürür : /// C: /% 20örnek ㄓ .txt

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.