How do I convert an absolute or relative URI path (e.g. /foo/bar.txt) to a (segmentwise) corresponding relative file system path (e.g. foo\bar.txt) in .NET?
My program is not an ASP.NET application.
Have you already tried Server.MapPath?
or Uri.LocalPath property? Something like following :
string uriString = "file://server/filename.ext";
// Lesson learnt - always check for a valid URI
if(Uri.IsWellFormedUriString(uriString))
{
Uri uri = new Uri(uriString);
Console.WriteLine(uri.LocalPath);
}
I figured out this way to produce a full absolute file system path from a relative or absolute URI and a base path.
With:
Uri basePathUri = new Uri(#"C:\abc\");
From a relative URI:
string filePath = new Uri(basePathUri, relativeUri).AbsolutePath;
From an absolute URI:
// baseUri is a URI used to derive a relative URI
Uri relativeUri = baseUri.MakeRelativeUri(absoluteUri);
string filePath = new Uri(basePathUri, relativeUri).AbsolutePath;
You can do this:
var localPath = Server.MapPath("/foo/bar.txt");
See MSDN for details
Not all have access to server.MapPath due to backend or framework changes, and there are lot's of way one of them is could be like this
public enum FileLocation
{
NotSet,
Disk,
Resource,
}
private static readonly string[] FileExtenstions = new[] {
".js"
,".ts"
,".vue"
,".css"
,".jpg"
,".png"
,".gif"
,".ico"
,".svg"
,".ttf"
,".eot"
,".ttf"
,".woff"
,".woff2"
,".mp4"
,".mp3"
,".emf"
};
public FileLocation IsMappedTo(Uri uri)
{
if (uri is null)
{
throw new ArgumentNullException(nameof(uri));
}
//make sure we support .net default URI contract
if (uri.IsFile)
return FileLocation.Disk;
//now assume you are looking in a web application
var path = uri.AbsolutePath;
if (path.Length == 0 || path.Equals("/",StringComparison.Ordinal) || path.Length< FileExtenstions.Min(s=>s.Length))
return FileLocation.NotSet;
//get the directory normally one would use IWebHostEnvironment.ContentRootPath different versions .net will have other methods
var dir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
//get all resources names from the assembly hosting this class out side if the loop from this assembly you can also use
//you can also use GetManifestResourceNames() to use the web application's assembly
var resourceNames = new HashSet<string>(this.GetType().Assembly.GetManifestResourceNames());
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null && entryAssembly != this.GetType().Assembly)
{
foreach (var entry in entryAssembly.GetManifestResourceNames())
{
if (string.IsNullOrEmpty(entry))
resourceNames.Add(entry);
}
}
for (var i = 0; i < FileExtenstions.Length; i++)
{
if (FileExtenstions[i].Equals(path[FileExtenstions[i].Length..], StringComparison.OrdinalIgnoreCase) || path.Contains(FileExtenstions[i], StringComparison.OrdinalIgnoreCase))
{
//exists on disk
if (File.Exists(Path.Combine(dir, path.Replace("/", #"\", StringComparison.Ordinal))))
return FileLocation.Disk;
//has a file as an embedded resource with the same name (ignores the path) so you might have duplicates names
if (resourceNames.Any(a => a.EndsWith(path.Split('/')[^1], StringComparison.OrdinalIgnoreCase)))
return FileLocation.Resource;
}
}
return FileLocation.NotSet;
}
after this you just do:
switch (IsMappedTo(url))
{
case FileLocation.NotSet:
break;
case FileLocation.Disk:
break;
case FileLocation.Resource:
break;
}
Related
I am trying to get the path of application "WinMergeU.exe" using Path.GetFullPath() but the return value of this code is the folder of my application.
How can i get the fullpath of the application in C: drive while the return value is d using the Path.GetFullPath().
You have to search for the file, e.g.
using system.Linq;
...
// Either full path of "WinMergeU.exe" file or null (if not found)
string result = Directory
.EnumerateFiles(#"c:\", "WinMergeU.exe", SearchOption.AllDirectories)
.FirstOrDefault();
Edit: In case the application is in the C:\Program Files (see comments) we can restrict the scan:
string result = Directory
.EnumerateFiles(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"WinMergeU.exe",
SearchOption.AllDirectories)
.FirstOrDefault();
Or (if we quite sure in c:\Program Files\WinMerge path) we can just test if the file exits:
string result = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"WinMerge",
"WinMergeU.exe");
if (File.Exists(result)) {
// we have the file
}
I installed it, as it looked like a handy tool, and searched the registry.
string key = #"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WinMerge.Project.File\shell\open\command";
string path = Microsoft.Win32.Registry.GetValue(key, "", -1);
... seems to contain the value you need. In my case:
"C:\Program Files (x86)\WinMerge\WinMergeU.exe" "%1"
The method will return -1 if the key is not found.
WinMerge doesn't allow to change its installation folder AFAIK, therefore searching the whole drive C is an overkill.
This will suffice:
static string GetWinMergeFullPath()
{
const string executableName = "WinMergeU.exe";
const string installationFolder = "WinMerge";
var locations = new Environment.SpecialFolder[]
{
Environment.SpecialFolder.ProgramFiles,
Environment.SpecialFolder.ProgramFilesX86
};
string fullPath = locations
.Select(l => Path.Combine(Environment.GetFolderPath(l), installationFolder, executableName))
.Where(File.Exists)
.FirstOrDefault();
return fullPath ?? throw new FileNotFoundException(executableName);
}
EDIT
As it was commented the above won't suffice:). The below version uses two registry locations that should contain the path. One is "App Paths" which is provided by the installation, the second one is "Uninstall". While I believe that "App Paths" should be enough I still provided the second one for robustness.Note that it will work even if shell integration is unchecked in the installer! Both 32-bit and 64-bit installations are covered.
static string GetWinMergeFullPathFromRegistryEx()
{
const string executableName = "WinMergeU.exe";
const string appPathKeyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
const string uninstallKeyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinMerge_is1";
const string installLocationName = "InstallLocation";
var locations = new List<(string path, string value, bool withFilename)>
{
($"{appPathKeyName}{executableName}", null, true),
(uninstallKeyName, installLocationName, false),
};
List<RegistryView> views = Environment.Is64BitOperatingSystem ?
new List<RegistryView>() { RegistryView.Registry32, RegistryView.Registry64 } :
new List<RegistryView>() { RegistryView.Default };
foreach (var view in views)
{
using (RegistryKey localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
foreach (var (path, value, withFilename) in locations)
{
using (RegistryKey key = localMachine.OpenSubKey(path, false))
{
var fullpathValue = key?.GetValue(value);
if (fullpathValue != null)
{
string fullpath = (string)fullpathValue;
if (!withFilename)
fullpath = Path.Combine(fullpath, executableName);
if (File.Exists(fullpath))
return fullpath;
}
}
}
}
throw new FileNotFoundException(executableName);
}
Of course my assumption is that WinMerge was actually installed. If you should support cases when WinMerge was deployed manually then you still can use to the full scan option as a fallback.
I'm hitting a local URL of the form: http://localhost/example.dev/eu/default.aspx.
My goal is to determine when the request is an aspx file inside of the global.asax file, and subsequently do stuff if it is an aspx file (and only an aspx file) using:
HttpContext.Current.Request.Url.IsFile
It's consistently resolving to false however and I'm not sure why. My complete global.asax code is:
if(HttpContext.Current.Request.Url.IsFile)
{
if(File.Exists(HttpContext.Current.Request.Url.LocalPath))
{
if(new FileInfo(HttpContext.Current.Request.Url.LocalPath).Extension.Equals("aspx"))
{
DoSomethingWithThePagesURL();
}
}
}
Did you take a look at the Documentation for IsFile Property?. It seems very clear from the documentation that Http: is not File:.
The IsFile property is true when the Scheme property equals UriSchemeFile.
DotNetFiddle Example
using System;
public class Program
{
public static void Main()
{
Uri uriAddress2 = new Uri("file://server/filename.ext");
Console.WriteLine(uriAddress2.LocalPath);
Console.WriteLine("Uri {0} a UNC path", uriAddress2.IsUnc ? "is" : "is not");
Console.WriteLine("Uri {0} a local host", uriAddress2.IsLoopback ? "is" : "is not");
Console.WriteLine("Uri {0} a file", uriAddress2.IsFile ? "is" : "is not");
}
}
Results:
\server\filename.ext
Uri is a UNC path
Uri is not a local host
Uri is a file
I use Nuget package walter.web.firewall that inject a IPageRequest in each request, this contains access to the requests underlying resource and will provide access via IPageRequest.LocalFile
However if you do need a firewall and it has been a while since this was asked, and lot's of framework changes have taken place since the question was asked so let my try and answer it in a way that uses no framework classes hoping it will work for all that try and implement it in the future.
here's the code:
public enum FileLocation
{
NotSet,
Disk,
Resource,
}
private static readonly string[] FileExtenstions = new[] {
".js"
,".ts"
,".vue"
,".css"
,".jpg"
,".png"
,".gif"
,".ico"
,".svg"
,".ttf"
,".eot"
,".ttf"
,".woff"
,".woff2"
,".mp4"
,".mp3"
,".emf"
};
public FileLocation IsMappedTo(Uri uri)
{
if (uri is null)
{
throw new ArgumentNullException(nameof(uri));
}
//make sure we support .net default URI contract
if (uri.IsFile)
return FileLocation.Disk;
//now assume you are looking in a web application
var path = uri.AbsolutePath;
if (path.Length == 0 || path.Equals("/",StringComparison.Ordinal) || path.Length< FileExtenstions.Min(s=>s.Length))
return FileLocation.NotSet;
//get the directory normally one would use IWebHostEnvironment.ContentRootPath different versions .net will have other methods
var dir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
//get all resources names from the assembly hosting this class out side if the loop from this assembly you can also use
//you can also use GetManifestResourceNames() to use the web application's assembly
var resourceNames = new HashSet<string>(this.GetType().Assembly.GetManifestResourceNames());
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null && entryAssembly != this.GetType().Assembly)
{
foreach (var entry in entryAssembly.GetManifestResourceNames())
{
if (string.IsNullOrEmpty(entry))
resourceNames.Add(entry);
}
}
for (var i = 0; i < FileExtenstions.Length; i++)
{
if (FileExtenstions[i].Equals(path[FileExtenstions[i].Length..], StringComparison.OrdinalIgnoreCase) || path.Contains(FileExtenstions[i], StringComparison.OrdinalIgnoreCase))
{
//exists on disk
if (File.Exists(Path.Combine(dir, path.Replace("/", #"\"))))
return FileLocation.Disk;
//has a file as an embedded resource with the same name (ignores the path) so you might have duplicates names
if (resourceNames.Any(a => a.EndsWith(path.Split('/')[^1], StringComparison.OrdinalIgnoreCase)))
return FileLocation.Resource;
}
}
return FileLocation.NotSet;
}
I am trying to set the uploadpaths for all of the RadEditor File Managers to the same absolute file path, and also share the same path across a couple of Solutions on the same machine.
So, I wrote a method to get the path from the Web.Config and set all the Properties on the FileManagerDialogConfiguration objects (UploadPaths, ViewPaths, etc). The problem is these properties are looking for Virtual Paths, and full paths don't work.
How can I supply the properties with the Virtual Path of a folder that may/may not be in the same Solution?
This didn't work:
private static FileManagerDialogConfiguration fixPaths(FileManagerDialogConfiguration f, String[] path)
{
if (path[0][0] != '~')
{
Uri basePath = new Uri(ConfigurationManager.AppSettings["veMainPath"]);
Uri absPath = new Uri(path[0]);
Uri relPath = basePath.MakeRelativeUri(absPath);
path[0] = relPath.LocalPath;
}
f.ViewPaths = path;
f.UploadPaths = path;
f.DeletePaths = path;
f.MaxUploadFileSize = 10485760;
return f;
}
How do I convert a relative path to an absolute path in a Windows application?
I know we can use server.MapPath() in ASP.NET. But what can we do in a Windows application?
I mean, if there is a .NET built-in function that can handle that...
Have you tried:
string absolute = Path.GetFullPath(relative);
? Note that that will use the current working directory of the process, not the directory containing the executable. If that doesn't help, please clarify your question.
If you want to get the path relative to your .exe then use
string absolute = Path.Combine(Application.ExecutablePath, relative);
This one works for paths on different drives, for drive-relative paths and for actual relative paths. Heck, it even works if the basePath isn't actually absolute; it always uses the current working directory as final fallback.
public static String GetAbsolutePath(String path)
{
return GetAbsolutePath(null, path);
}
public static String GetAbsolutePath(String basePath, String path)
{
if (path == null)
return null;
if (basePath == null)
basePath = Path.GetFullPath("."); // quick way of getting current working directory
else
basePath = GetAbsolutePath(null, basePath); // to be REALLY sure ;)
String finalPath;
// specific for windows paths starting on \ - they need the drive added to them.
// I constructed this piece like this for possible Mono support.
if (!Path.IsPathRooted(path) || "\\".Equals(Path.GetPathRoot(path)))
{
if (path.StartsWith(Path.DirectorySeparatorChar.ToString()))
finalPath = Path.Combine(Path.GetPathRoot(basePath), path.TrimStart(Path.DirectorySeparatorChar));
else
finalPath = Path.Combine(basePath, path);
}
else
finalPath = path;
// resolves any internal "..\" to get the true full path.
return Path.GetFullPath(finalPath);
}
It's a bit older topic, but it might be useful for someone.
I have solved a similar problem, but in my case, the path was not at the beginning of the text.
So here is my solution:
public static class StringExtension
{
private const string parentSymbol = "..\\";
private const string absoluteSymbol = ".\\";
public static String AbsolutePath(this string relativePath)
{
string replacePath = AppDomain.CurrentDomain.BaseDirectory;
int parentStart = relativePath.IndexOf(parentSymbol);
int absoluteStart = relativePath.IndexOf(absoluteSymbol);
if (parentStart >= 0)
{
int parentLength = 0;
while (relativePath.Substring(parentStart + parentLength).Contains(parentSymbol))
{
replacePath = new DirectoryInfo(replacePath).Parent.FullName;
parentLength = parentLength + parentSymbol.Length;
};
relativePath = relativePath.Replace(relativePath.Substring(parentStart, parentLength), string.Format("{0}\\", replacePath));
}
else if (absoluteStart >= 0)
{
relativePath = relativePath.Replace(".\\", replacePath);
}
return relativePath;
}
}
Example:
Data Source=.\Data\Data.sdf;Persist Security Info=False;
Data Source=..\..\bin\Debug\Data\Data.sdf;Persist Security Info=False;
I'm trying to join a Windows path with a relative path using Path.Combine.
However, Path.Combine(#"C:\blah",#"..\bling") returns C:\blah\..\bling instead of C:\bling\.
Does anyone know how to accomplish this without writing my own relative path resolver (which shouldn't be too hard)?
What Works:
string relativePath = "..\\bling.txt";
string baseDirectory = "C:\\blah\\";
string absolutePath = Path.GetFullPath(baseDirectory + relativePath);
(result: absolutePath="C:\bling.txt")
What doesn't work
string relativePath = "..\\bling.txt";
Uri baseAbsoluteUri = new Uri("C:\\blah\\");
string absolutePath = new Uri(baseAbsoluteUri, relativePath).AbsolutePath;
(result: absolutePath="C:/blah/bling.txt")
Call Path.GetFullPath on the combined path http://msdn.microsoft.com/en-us/library/system.io.path.getfullpath.aspx
> Path.GetFullPath(Path.Combine(#"C:\blah\",#"..\bling"))
C:\bling
(I agree Path.Combine ought to do this by itself)
Path.GetFullPath(#"c:\windows\temp\..\system32")?
For windows universal apps Path.GetFullPath() is not available, you can use the System.Uri class instead:
Uri uri = new Uri(Path.Combine(#"C:\blah\",#"..\bling"));
Console.WriteLine(uri.LocalPath);
This will give you exactly what you need (path does NOT have to exist for this to work)
DirectoryInfo di = new DirectoryInfo(#"C:\blah\..\bling");
string cleanPath = di.FullName;
Path.GetFullPath() does not work with relative paths.
Here's the solution that works with both relative + absolute paths. It works on both Linux + Windows and it keeps the .. as expected in the beginning of the text (at rest they will be normalized). The solution still relies on Path.GetFullPath to do the fix with a small workaround.
It's an extension method so use it like text.Canonicalize()
/// <summary>
/// Fixes "../.." etc
/// </summary>
public static string Canonicalize(this string path)
{
if (path.IsAbsolutePath())
return Path.GetFullPath(path);
var fakeRoot = Environment.CurrentDirectory; // Gives us a cross platform full path
var combined = Path.Combine(fakeRoot, path);
combined = Path.GetFullPath(combined);
return combined.RelativeTo(fakeRoot);
}
private static bool IsAbsolutePath(this string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
return
Path.IsPathRooted(path)
&& !Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)
&& !Path.GetPathRoot(path).Equals(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal);
}
private static string RelativeTo(this string filespec, string folder)
{
var pathUri = new Uri(filespec);
// Folders must end in a slash
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) folder += Path.DirectorySeparatorChar;
var folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString()
.Replace('/', Path.DirectorySeparatorChar));
}
Be careful with Backslashes, don't forget them (neither use twice:)
string relativePath = "..\\bling.txt";
string baseDirectory = "C:\\blah\\";
//OR:
//string relativePath = "\\..\\bling.txt";
//string baseDirectory = "C:\\blah";
//THEN
string absolutePath = Path.GetFullPath(baseDirectory + relativePath);