Перетворити шлях до файлу URI?


201

Чи є у .NET Framework якісь методи перетворення шляху (наприклад "C:\whatever.txt") у URI файлу (наприклад "file:///C:/whatever.txt")?

Клас System.Uri має зворотний (від URI файлу до абсолютного шляху), але нічого, наскільки я можу знайти для перетворення в URI файлу.

Також це не програма ASP.NET.

Відповіді:


291

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 назад у локальну файлову дорогу для тих, хто цього потребує.
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 \ what.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

Я переконаний, що це страждає від тих самих проблем, які описані у відповіді, що говорить про особливу обробку персонажів .
бінкі

4

Принаймні в .NET 4.5+ ви також можете зробити:

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

1
Ви не ризикуєте отримати UriFormatExceptionодин день?
Березовський

Це також не працює належним чином, 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-адреси, навіть якщо буфер менший, тому я спочатку отримую довжину, а потім виділяю буфер.

У мене було дуже цікаве зауваження, що в той час як "\\ пристрій \ шлях" правильно трансформується у "файл: // пристрій / шлях", конкретно "\\ localhost \ шлях" перетворюється на просто "файл: /// шлях" .

Функція WinApi спромоглася кодувати спеціальні символи, але залишає символи, характерні для Unicode, незашифрованими, на відміну від конструктора Uri . У цьому випадку AbsoluteUri містить правильно закодовану URL-адресу, тоді як OriginalString може використовуватися для збереження символів Unicode.


0

Вирішення проблем просте. Просто скористайтеся методом Uri (). ToString () та згодом прокодуйте пробіли, якщо такі є, після чого.

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

належним чином повертає файл: /// C: / my% 20example ㄓ .txt

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.