简体   繁体   中英

Path.Combine for URLs (part 2)

For awhile now, I've been searching for a Path.Combine method that works on URLs. This is similiar to Path.Combine for URLs? with one big difference.

I'll illustrate with an example. Say we have a base url: http://example.com/somefolder and a file: foo.txt . Thus, the full path would be: http://example.com/somefolder/foo.txt . Sounds simple, right? Ha.

I tried the Uri class: Uri.TryCreate(new Uri("http://example.com/somefolder"), "foo.txt", out x); which resulted in "http://example.com/foo.txt" .

Then I tried Path: System.IO.Path.Combine("http://example.com/somefolder", "foo.txt"); which resulted in "http://example.com/somefolder\\foo.txt" ... Closer, but still no.

For kicks, I then tried: System.IO.Path.Combine("http://example.com/somefolder/", "foo.txt") which resulted in "http://example.com/somefolder/foo.txt" .

The last one worked, but it's basically doing string concatenation at that point.

So I think I have two options:

  • Use Path.Combine and replace all \\ with /
  • Use basic string concatenation

Am I missing a built in framework method for this?

UPDATE: The usage case I have is for downloading a bunch of files. My code looks like this:

    public void Download()
    {
        var folder = "http://example.com/somefolder";
        var filenames = getFileNames(folder);

        foreach (var name in filenames)
        {
            downloadFile(new Uri(folder + "/" + name));
        }
    }

I'm miffed at having to use string concat in the Uri constructor, as well having to check if the slash is needed (which I omitted in the code).

It seems to me that what I'm trying to do would come up a lot, since the Uri class handles a lot of other protocols besides http.

Flurl [disclosure: I'm the author] is a tiny URL builder library that can fill the gap with its Url.Combine method:

string url = Url.Combine("http://www.foo.com/", "/too/", "/many/", "/slashes/", "too", "few");
// result: "http://www.foo.com/too/many/slashes/too/few"

You can get it via NuGet: Install-Package Flurl .

I also wanted to point out that you can dramatically improve the efficiency of your code by downloading the files in parallel. There's a couple ways to do that. If you're on .NET 4.5 or above and can rewrite downloadFile as an async method, then your best option would be to replace your for loop with something like this:

var tasks = filenames.Select(f => downloadFileAsync(Url.Combine(folder, f)));
await Task.WhenAll(tasks);

Otherwise, if you're stuck on .NET 4, you can still achieve parallelism easily by using Parallel.ForEach :

Parallel.ForEach(filenames, f => downloadFile(Url.Combine(folder, f)));

This is how the Uri class works.

var otherUri = new Uri("http://example.com/somefolder"));
// somefolder is just a path
var somefolder = otherUri.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped);

// example one
var baseUri = new Uri("http://example.com/");   
var relativeUri = new Uri("somefolder/file.txt",UriKind.Relative);
var fullUri = new Uri(baseUri, relativeUri);

// example two
var baseUri = new Uri("http://example.com/somefolder"); 
var relativeUri = new Uri("somefolder/file.txt",UriKind.Relative);
var fullUri = new Uri(baseUri, relativeUri);

// example three
var baseUri = new Uri("http://example.com/");   
var fullUri = new Uri(baseUri, "somefolder/file.txt");  

Basically do it via string manipulation simplest and do

var isValid = Uri.TryCreate(..., out myUri);

If you want to find out more. Check out this post C# Url Builder Class

Updated answer

When referring to base uri it will always be http://example.com/ anything to the right is just path.

void Main()
{
    var ub = new UriBuilder("http://example.com/somefolder");
    ub.AddPath("file.txt"); 
            var fullUri = ub.Uri;
}
public static class MyExtensions
{
    public static UriBuilder AddPath(this UriBuilder builder, string pathValue)
    {
    var path = builder.Path;

    if (path.EndsWith("/") == false)
    {
        path = path + "/";
    }

    path += Uri.EscapeDataString(pathValue);

    builder.Path = path;
    }
}

I have a static method for this purpose:

// Combines urls like System.IO.Path.Combine
// Usage: this.Literal1.Text = CommonCode.UrlCombine("http://stackoverflow.com/", "/questions ", " 372865", "path-combine-for-urls");
public static string UrlCombine(params string[] urls) {
    string retVal = string.Empty;
    foreach (string url in urls)
    {
        var path = url.Trim().TrimEnd('/').TrimStart('/').Trim();
        retVal = string.IsNullOrWhiteSpace(retVal) ? path : new System.Uri(new System.Uri(retVal + "/"), path).ToString();
    }
    return retVal;

}

Here is a LINQ version of something close to the above answer.

public static string UrlCombine( this string root, params string[] parts)
{
    return parts
        .Select(part => part.Trim().TrimEnd('/').TrimStart('/').Trim())
        .Aggregate(root, (current, path) => current + ("/" + path));
}

var x = "http://domain.com";

var p = "path";

var u = x.UrlCombine(p, "test.html"); // http://domain.com/path/test.html

Omg, why all of you write such complex code? It is very simple:

private string CombineUrl(params string[] urls)
{
    string result = "";

    foreach (var url in urls)
    {
        if (result.Length > 0 && url.Length > 0)
            result += '/';

        result += url.Trim('/');
    }

    return result;
}

Example of use:

var methodUrl = CombineUrl("http://something.com", "/task/status/", "dfgd/", "/111", "qqq");

Result url is " http://something.com/task/status/dfgd/111/qqq "

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM