Different slashes in web application paths - c#

Why if I need to check if a folder exists on the web server I need to use backslashes in the path string:
if (Directory.Exists(#"~\assets\images\gallery\thumbnails"))
while to load the image I need to use slashes?
new WebImage("~/assets/images/gallery/thumbnail/name.jpg")
What's rule behind this?
Thanks.

Directory.Exists is a check against file system, which on Windows follows Windows path specification (the link is not exactly "specification," but will do for the purpose) which has \ as directory separator. On *nix systems, however, it would take / as directory separator. Thus, one should use Path.DirectorySeparatorChar for correctness and interoperability.
WebImage takes a URL reference (~ notwithstanding) and follows RFC 1738 which defines / as segment separator.
The two, while appearing similar, are different and (mostly) unrelated concepts.

For URLs you use forward slashes. In file paths in Windows you use backslashes. The first path is to a location on the hard drive. The second path is to a URL.

I believe WebImage refers to a virtual path, as used in URLs and referred to by the browser (for example).
Directory refers to actual physical directories on the server.
Windows uses backslash for physical directories.
URL conventions use slash for virtual paths.

Related

Can't get to files with multiple consecutive spaces at the end of a FOLDER name

I'm trying to programmaticaly locate a file on a network server where there are multiple directory levels, one of which has two spaces at the end of the name. EG:
\\MyServer\C$\TopLevel\Account \someone#somewhere.com\AFileName.pdf
Note that the Account level represents many folders using account codes for the folder name, and most of them have the two spaces at the end.
I can't amend the directory structure as it's used by other processes outside my control. I will know in advance the path down to and including the Account level, so I need to search the Email level subfolders for a file containing an order number in the file name. I also know the order number.
The problem is that FileInfo, Directory and DirectoryInfo all balk at the two spaces. Whilst Windows has allowed the folders to be created, and an automated third party process can create folders and files below the Account level, nothing I have tried so far has worked because the spaces get stripped out by the c# file and directory classes.
I can't use:
Directory.GetFiles(PathToTopLevel, "*.pdf")
because there are over 1000 Account level folders, each of which might have 5 or 6 Email level folders with possibly many hundreds of pdf files in each one. I did try it but had to kill it when it was still thinking about it after 5 minutes.
Directory.GetFiles(PathToAccountLevel, "*.pdf")
This returns the 'can't find part of the path' error.
I also tried:
filePath = Path.Combine(PathToAccountLevel, $"{order_no}.pdf");
var fi = new FileInfo(filePath);
which also returns 'can't find part of the path'.
I've done lots of trawling through SO and other forums with no success as they are mainly concerned either with file names or single spaces, but I did find a useful method here which I have tried:
filePath = AddQuotesIfRequired(PathToAccountLevel);
Directory.GetFiles(filePath, "*.pdf")
Which does indeed add the quotes, but it makes no difference to the outcome.
Also tried (with and without adding quotes):
Directory.GetFiles(#filePath, "*.pdf")
I've tried as many combinations as I can think of using quotes and '#' etc. but absolutely nothing has worked so far. I've even hard coded the full path down to the Email level and still no joy.
I'm using C# with VS2019 on Windows 10. The server is running Windows Server 2012 R2 DataCenter. I've run the test executable locally on the server and the results are the same.
The really galling aspect of this is that a simple test program written in delphi and using FindFirst/Next works perfectly!
I should also mention that permissions are not an issue, and the files exist.
Any help at all would be great, thanks.
Re the possible duplicate/proposed answer from qaabaam:
Answer 1 for that question is not valid code - the line
DirectoryInfo wantedDir = tempWithoutMac.GetDirectories.Where(c => c.Contains(MacID)).First();
throws an error for GetDirectories being a method and not valid in the context. If you change it to GetDirectories() it then complains that DirectoryInfo does not have a definition for Contains.
Answer 2 using alt 0160 still throws the can't find part of the path error.
Answer 3 is basically an opinion and doesn't help at all.
If you can figure out how to get Answer 1 to work I'll definitely give it a try!
Leading or trailing spaces in directory names is one of those grey areas in Windows: you can do it, but you probably shouldn't. The reason for this is simple - the vast majority of the time, a leading or trailing space is a typo, and the Windows APIs around paths have been designed to take this into account, to the point where (as you've discovered) they will outright ignore leading or trailing spaces. (For example, in Windows 10, it's literally impossible to delete a directory with trailing spaces via Windows Explorer!) Since C# is built on top of these APIs, it inherits their "quirks".
Fortunately there is a way to tell Windows "don't try to fix this path, it is correct, I know what I'm doing" via the use of DOS device paths. In brief:
For local paths, prefix them with the literal \\?\. In other words, C:\foo\bar would become \\?\C:\foo\bar.
For UNC paths, replace the leading \\ with \\?\UNC\. In other words, \\server\foo\bar would become \\?\UNC\server\foo\bar.
In your case, then, all you need to do is change \\MyServer\C$\TopLevel\Account \ to \\?\UNC\MyServer\C$\TopLevel\Account \ and everything should work as you expect.
As an aside, what you are doing is a document search/query operation, which will be incredibly slow as it's performing I/O, and over the network at that. You'd be far better served in writing a tool to migrate these documents into a proper database, then searching that database for the documents.

Are backslashes at the end of folder paths relevant?

I am writing a program that copies all files and directories from a source location into a target location. Both locations are provided by the user.
I was just thinking about checking whether the location paths have a backslash "\" at the end, when I decided to run some tests and from what I see it doesn't matter if you tell c#
File.Copy("C:\\test", "D:\\test")`
or
File.Copy("C:\\test\\", "D:\\test\\")`
Am I wrong? Does a backslash at the end of a file path matter?
Update:
On Windows, you apparently cannot name a file and folder the same, so it won't matter there. You should still use a (back)slash for good style and cross-platform compatability. Some software or libraries might even depend on it.
Original Answer:
A folder is generally ending in \ (or /), because there might be a file with no extension, named test. For example, these two could coexist (Linux [notice the different capitalization]):
Folder: /Users/nikxda/Docouments/Test/
File: /Users/nikxda/Docouments/test
If you work case-sensitive, then you could in fact ignore it on Windows. I'd still recommend using a trailing slash, just for
Clarification (specify it is a folder)
Good style (following conventions)
Compatibility (other software, libraries, etc. might depend on it)
Cross platform (OSX, ...)
So yes, you should always use a (back)slash at the end of your path if referring to folders.
It should make no difference, because - on windows - you cannot have a folder "test" and file named "test" without extension in the same parent directory at the same time.
However, it will be much more cleaner to have a trailing backslash within your code, because this leaves no room for speculation. And it will be better for cross-plattform purposes as well.
Thinking of URIs for example, it can definitely make a difference between a trailing slash or no trailing slash, by the way.
TLDR: Use a trailing (back)slash, please :-)

How to get minimal absolute path from relative path without any permission checks?

Say, I have two path strings, an absolute one such as #"C:\abc\xyz", and a relative one such as #"..\def". How do I reliably combine those to yield the minimal form #"C:\abc\def"?
As the process should work for any forms of path that .NET's I/O API supports (i.e. the native paths of the system that .NET or Mono is currently running on, or alternatively something like UNC paths and the like), manual string manipulation seems to be too unreliably a solution.
In general, the tidy way to combine path strings is to use the Path.Combine method:
Path.Combine(#"C:\abc\xyz", #"..\def")
Unfortunately, it does not minimize the path and returns C:\abc\xyz\..\def.
As is suggested in a couple of other questions (e.g. this, or this), the GetFullPath method should be applied to the result:
Path.GetFullPath(Path.Combine(#"C:\abc\xyz", #"..\def"))
The problem with this is that GetFullPath actually looks at the file system rather than just handling the path string. From the docs:
The file or directory specified by path is not required to exist. (...) However, if path does exist, the caller must have permission to obtain path information for path. Note that unlike most members of the Path class, this method accesses the file system.
Therefore, GetFullPath doesn't work if I just want to minimize arbitrary path strings: Depending on whether the path happens to exist on the system where the application is running, the method might fail with a SecurityException if the user does not have access to the path.
So, how do I reliably combine path strings that could be processed by System.IO methods to return the shortest possible absolute path?
You can use AbsolutePath of Uri class
var path = new Uri(Path.Combine(#"C:\abc\xyz", #"..\def")).AbsolutePath;
If you want to use it via System.IO you will run into access violation problems at that point.
Why bother to increase the time till the exception comes?
Use try {} catch {} around GetFullPath and handle the error in place would be my suggestion.
So, how do I reliably combine path strings that could be processed by System.IO methods to return the shortest possible absolute path?

Use Process.Start to execute a File on a Shared Folder

I'm trying to start a new Process by using Process.Start() which works great when I pass in
Process.Start("C:\\Documents and Settings\\Upload.exe")
but is it possible to perform that same operation when I move Upload.exe into a shared folder under My Network Places? I tried
Process.Start("\\Shared Folder\\Upload.exe");
but I get a Win32Exception. Thanks for any information or suggestions in advance.
You should use UNC path for accessing a network resource. (Your file becomes a network resource when you place it in a shared path)
UNC path takes the following form.
\\ServerName\SharedPath\YourFile.exe
or
\\ServerName\D$\SharedPath\YourFile.exe
where D$ is the drive letter.
In your case you may have to use the following
Process.Start(#"\\Server-Name\Shared Folder\Upload.exe");
Use # symbol in front of the string because your \\ will be treated as \, as an escape character.
Try either: "\\\\Shared Folder\\Upload.exe" or #"\\Shared Folder\Upload.exe"

IIS/HttpHandler: Get relative absolute path?

I've got a site setup in IIS to run at http://localhost/WebApplication6. In my web application I have a handler (implements IHttpHandler). When I print context.Request.Url.AbsolutePath, I get /WebApplication6/whaetever. I want to trim off /WebApplication6 (the local site name). How can I do that? Is there a way to get the "WebApplication6" bit so I know what to trim off? (inside IHttpHandler.ProcessRequest).
Your best bet would be HttpRequest.AppRelativeCurrentExecutionFilePath - it provides path relative to your web application root directory. However, it will be in form of "~/whatever" where ~/ indicates app relative path. If your requirement is to get /whatever then you can strip off ~ using string functions.
BTW, here's good article that will help you make sense of all paths: http://www.west-wind.com/weblog/posts/132081.aspx
VirtualPathUtility.GetDirectory(context.Request.Url.AbsolutePath)

Categories

Resources