9

I need to add some additional query information to file path as query parameter to parse path later during files processing. I though that System.Uri class can help me with this, but it looks like it doesn't give me what I expected for local file paths.

var fileUri = new Uri("file:///c://a.txt?select=10")
// fileUri.AbsoluteUri = "file:///c://a.txt%3Fselect=10"
// fileUri.Query = ""

var httpUri = new Uri("http://someAddress/a.txt?select=10")
// httpUri.AbsoluteUri = "http://someaddress/a.txt?select=10"
// httpUri.Query = "?select=10"

In the case of "ftp://someAddress/a.txt?select=10" - query string is also empty

I understand that System.Uri probably resolves "a.txt?select=10" to correct file name "a.txt%3Fselect=10", but WHY - how to escape this?

Thanks in advance

iwein
  • 25,788
  • 10
  • 70
  • 111
Vitaliy
  • 2,744
  • 1
  • 24
  • 39
  • 1
    @Oded Since they are Uris. And Uris have parameters. – ordag Jan 06 '12 at 12:25
  • @ordag - My point is that FTP servers and the OS will not do _anything_ with these parameters. – Oded Jan 06 '12 at 12:27
  • 2
    @Oded That's true, they are valid though. And the author wants to parse that parameters himself in later processing. – ordag Jan 06 '12 at 12:31
  • 1
    @Oded - yes ftp-servers will not do anything with parameters - but as I said in question that I was going to store query parameters in file path to use them later (parse path string and react according to the data after the "?" character) – Vitaliy Jan 06 '12 at 12:32

2 Answers2

10

This is a bug which Microsoft won't fix : Bug 594562 As you can see they propose reflection as an workaround:

...
Console.WriteLine("Before");
Uri fileUri = new Uri("file://host/path/file?query#fragment");
Console.WriteLine("AbsoluteUri: " + fileUri.AbsoluteUri);
Console.WriteLine("ToString: " + fileUri.ToString());
Console.WriteLine("LocalPath: " + fileUri.LocalPath);
Console.WriteLine("Query: " + fileUri.Query);
Console.WriteLine("Fragment: " + fileUri.Fragment);

Type uriParserType = typeof(UriParser);
FieldInfo fileParserInfo = uriParserType.GetField("FileUri", BindingFlags.Static | BindingFlags.NonPublic);
UriParser fileParser = (UriParser)fileParserInfo.GetValue(null);
FieldInfo fileFlagsInfo = uriParserType.GetField("m_Flags", BindingFlags.NonPublic | BindingFlags.Instance);
int fileFlags = (int)fileFlagsInfo.GetValue(fileParser);
int mayHaveQuery = 0x20;
fileFlags |= mayHaveQuery;
fileFlagsInfo.SetValue(fileParser, fileFlags);

Console.WriteLine();
Console.WriteLine("After");
fileUri = new Uri("file://host/path/file?query#fragment");
Console.WriteLine("AbsoluteUri: " + fileUri.AbsoluteUri);
Console.WriteLine("ToString: " + fileUri.ToString());
Console.WriteLine("LocalPath: " + fileUri.LocalPath);
Console.WriteLine("Query: " + fileUri.Query);
Console.WriteLine("Fragment: " + fileUri.Fragment);  
...  
Vitaliy
  • 2,744
  • 1
  • 24
  • 39
VMykyt
  • 1,589
  • 12
  • 17
5

Query string parameters are not valid when requesting a local file.

When you requests a file using http the file is executed and is therefore able to read and process the querystring. When you request a local file it is not executed and so is unable to make use of the querystring.

What is the reason you are adding querystring params to a file request? Is there another way of doing it?

Oded
  • 489,969
  • 99
  • 883
  • 1,009
Fran Hoey
  • 895
  • 9
  • 18
  • The reason why I'm using such construction is to make simple and understandable query - for example access to the *files* folder *txt* files - "files/?select=*.txt", access to the 10th line in the file "file.txt?line=10" – Vitaliy Jan 06 '12 at 12:40
  • Do you mean that when I call Uri constructor - it really tries to access file? I thought that it does only url parsing into parts - I understand that probably *ftp* and *file* protocols doesn't have usage for query parameters - and Uri class provides some interal path resolving. But as @ordag mentioned - they are Uris and Uris supports query parameters – Vitaliy Jan 06 '12 at 12:48
  • And again - similar query constructions are used in XSLT (for example with fn:collection function) - it is allowed to write following XPath "count(collection('file:///d:/collection/?select=*.xml'))" – Vitaliy Jan 06 '12 at 12:52
  • It's all about how you are processing the request. If you entered file:// in a browser it would access the file without processing it (e.g. to return the 10th line). Therefore the URI is assuming the querystring parameters in a file:// url are part of the file name. Are you simply using URI as a method of storing the file path to be processed by some code? if so then you will need to either remove the file:// or use a simple string and parse it manually. – Fran Hoey Jan 06 '12 at 13:51
  • 1
    I was going to use Uri class for path parsing - I mean to get 2 values: absolute file path and query parameters. I'm not going to use "file:///c://a.txt?select=10" from browser or somewhere else. If i remove "file://" from the string - var fileUri = new Uri("c://a.txt?select=10") - will again be internally resolved to "file:///c://a.txt%3Fselect=10" – Vitaliy Jan 06 '12 at 14:17
  • Thanks for you help. As @VMykyt said - this is a bug in Uri class – Vitaliy Jan 06 '12 at 14:27