development

파일 경로를 파일 URI로 변환 하시겠습니까?

big-blog 2020. 5. 13. 20:47
반응형

파일 경로를 파일 URI로 변환 하시겠습니까?


.NET Framework에 경로 (예 "C:\whatever.txt":)를 파일 URI (예 :)로 변환하는 방법이 "file:///C:/whatever.txt"있습니까?

선택 System.Uri의 클래스는 (절대 경로에 파일 URI)에서 반대가 있지만 아무것도까지 내가 파일 URI로 변환 찾을 수있다.

또한 이것은 ASP.NET 응용 프로그램 아닙니다 .


System.Uri생성자는 전체 파일 경로를 분석하고 URI 스타일의 경로로를 설정하는 기능이 있습니다. 따라서 다음을 수행 할 수 있습니다.

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

아무도 알지 못하는 것은 어떤 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을 형식의 의사 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에서 수행되는 방식 인 것처럼 + 및 : 인코딩되지 않은 채로 둡니다. Internet Explorer가 인코딩 된 파일 URL의 유니 코드 문자를 이해할 수 없으므로 latin1 만 인코딩합니다.


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


위의 솔루션은 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"


.NET 4.5 이상에서는 다음을 수행 할 수도 있습니다.

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

구조에 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);

In case the path starts with with a special prefix, it gets removed. Although the documentation doesn't mention it, the function outputs the length of the URL even if the buffer is smaller, so I first obtain the length and then allocate the buffer.

Some very interesting observation I had is that while "\\device\path" is correctly transformed to "file://device/path", specifically "\\localhost\path" is transformed to just "file:///path".

The WinApi function managed to encode special characters, but leaves Unicode-specific characters unencoded, unlike the Uri construtor. In that case, AbsoluteUri contains the properly encoded URL, while OriginalString can be used to retain the Unicode characters.


The workaround is simple. Just use the Uri().ToString() method and percent-encode white-spaces, if any, afterwards.

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

properly returns file:///C:/my%20exampleㄓ.txt

참고URL : https://stackoverflow.com/questions/1546419/convert-file-path-to-a-file-uri

반응형