Преобразовать путь к файлу в URI файла?

202

Есть ли в .NET Framework какие-либо методы для преобразования пути (например, "C:\whatever.txt" ) в URI файла (например "file:///C:/whatever.txt")?

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

Кроме того, это не приложение ASP.NET.

Tinister
источник

Ответы:

293

System.UriКонструктор имеет возможность анализировать полные пути к файлам и превратить их в дорожки стиля URI. Так что вы можете просто сделать следующее:

var uri = new System.Uri("c:\\foo");
var converted = uri.AbsoluteUri;
JaredPar
источник
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
Также это не будет работать с путями, которые содержат символ #.
Льюис
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. Он также кодирует только латиницу 1, поскольку Internet Explorer не может понимать символы Юникода в URL-адресах файлов, если они закодированы.

poizan42
источник
Есть ли в Nuget это с либеральной лицензией? Жаль, что в фреймворке нет подходящего способа, и постоянно обновлять 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()IS"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

MrCalvin
источник
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, даже если буфер меньше, поэтому я сначала получаю длину, а затем выделяю буфер.

У меня было очень интересное наблюдение: «\\ device \ path» правильно преобразуется в «file: // device / path», в частности «\\ localhost \ path» преобразуется в просто «file: /// path» ,

Функция WinApi сумела кодировать специальные символы, но оставляет символы, не связанные с Unicode, в отличие от Uri-конструктора . В этом случае AbsoluteUri содержит правильно закодированный URL, а OriginalString можно использовать для сохранения символов Unicode.

IllidanS4 хочет вернуть Монику
источник
0

Обходной путь прост. Просто используйте метод Uri (). ToString () и запишите процентные пробелы, если таковые имеются, впоследствии.

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

правильно возвращает файл: /// C: / my% 20example ㄓ .txt

Всякое случается
источник