.NET Frameworkには、パス("C:\whatever.txt"
など)をファイルURI("file:///C:/whatever.txt"
など)に変換するためのメソッドがありますか?
System.Uri クラスには逆(ファイルURIから絶対パスへ)がありますが、ファイルURIに変換するために見つけることができるものはありません。
また、これはnotASP.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
になります。どういうわけか、someシーケンスをデコードし、他のシーケンスはデコードしないと決定します。これで自分で"file:///"
をプレフィックスすることができますが、これは\\remote\share\foo.txt
のようなUNCパスを考慮に入れません-Windowsで一般的に受け入れられているように思われるのは、フォームをfile://remote/share/foo.txt
の擬似URLに変換することですので、まあ。
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のUnicode文字を理解できないため、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
1つのライナー:
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()
... emits "file:///C:/Users/foo/README.md"
少なくとも.NET 4.5以降では、次のこともできます。
var uri = new System.Uri("C:\\foo", UriKind.Absolute);
rlCreateFromPath 救助に!拡張された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の長さを出力するため、最初に長さを取得してからバッファを割り当てます。
いくつかのvery興味深い観察は、「\\ device\path」が「file:// device/path」、特に「 \\ localhost\path "は、" file:/// path "に変換されます。
WinApi関数は特殊文字をエンコードできましたが、Uriコンストラクターとは異なり、Unicode固有の文字はエンコードされません。その場合、AbsoluteUriには適切にエンコードされたURLが含まれますが、OriginalStringはUnicode文字を保持するために使用されます。
回避策は簡単です。あとでUri()。ToString()メソッドを使用し、空白がある場合はパーセントエンコードします。
string path = new Uri("C:\my exampleㄓ.txt").ToString().Replace(" ", "%20");
適切にfile:/// C:/ my%20exampleㄓ.txtを返します