[c#] Convert file path to a file URI?

Does the .NET Framework have any methods for converting a path (e.g. "C:\whatever.txt") into a file URI (e.g. "file:///C:/whatever.txt")?

The System.Uri class has the reverse (from a file URI to absolute path), but nothing as far as I can find for converting to a file URI.

Also, this is not an ASP.NET application.

This question is related to c# .net path uri

The answer is


The System.Uri constructor has the ability to parse full file paths and turn them into URI style paths. So you can just do the following:

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

The solutions above do not work on Linux.

Using .NET Core, attempting to execute new Uri("/home/foo/README.md") results in an exception:

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

You need to give the CLR some hints about what sort of URL you have.

This works:

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

...and the string returned by fileUri.ToString() is "file:///home/foo/README.md"

This works on Windows, too.

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

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


UrlCreateFromPath to the rescue! Well, not entirely, as it doesn't support extended and UNC path formats, but that's not so hard to overcome:

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.


What no-one seems to realize is that none of the System.Uri constructors correctly handles certain paths with percent signs in them.

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

This gives you "file:///C:/Q.txt" instead of "file:///C:/%2551.txt".

Neither values of the deprecated dontEscape argument makes any difference, and specifying the UriKind gives the same result too. Trying with the UriBuilder doesn't help either:

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

This returns "file:///C:/Q.txt" as well.

As far as I can tell the framework is actually lacking any way of doing this correctly.

We can try to it by replacing the backslashes with forward slashes and feed the path to Uri.EscapeUriString - i.e.

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

This seems to work at first, but if you give it the path C:\a b.txt then you end up with file:///C:/a%2520b.txt instead of file:///C:/a%20b.txt - somehow it decides that some sequences should be decoded but not others. Now we could just prefix with "file:///" ourselves, however this fails to take UNC paths like \\remote\share\foo.txt into account - what seems to be generally accepted on Windows is to turn them into pseudo-urls of the form file://remote/share/foo.txt, so we should take that into account as well.

EscapeUriString also has the problem that it does not escape the '#' character. It would seem at this point that we have no other choice but making our own method from scratch. So this is what I suggest:

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

This intentionally leaves + and : unencoded as that seems to be how it's usually done on Windows. It also only encodes latin1 as Internet Explorer can't understand unicode characters in file urls if they are encoded.


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


VB.NET:

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

Different outputs:

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

One liner:

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

Output: file:///D:/Development/~AppFolder/Att/1.gif


At least in .NET 4.5+ you can also do:

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

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to path

Get Path from another app (WhatsApp) How to serve up images in Angular2? How to create multiple output paths in Webpack config Setting the correct PATH for Eclipse How to change the Jupyter start-up folder Setting up enviromental variables in Windows 10 to use java and javac How do I edit $PATH (.bash_profile) on OSX? Can't find SDK folder inside Android studio path, and SDK manager not opening Get the directory from a file path in java (android) Graphviz's executables are not found (Python 3.4)

Examples related to uri

Get Path from another app (WhatsApp) What is the difference between resource and endpoint? Convert a file path to Uri in Android How do I delete files programmatically on Android? java.net.MalformedURLException: no protocol on URL based on a string modified with URLEncoder How to get id from URL in codeigniter? Get real path from URI, Android KitKat new storage access framework Use URI builder in Android or create URL with variables Converting of Uri to String How are parameters sent in an HTTP POST request?