Path.Combine для URL-адресов (часть 2)

На какое-то время я искал метод Path.Combine, который работает с URL-адресами. Это похоже на Path.Combine для URL-адресов? с одной большой разницей.

Я проиллюстрирую пример. Скажем, у нас есть базовый url: http://example.com/somefolder и файл: foo.txt. Таким образом, полный путь: http://example.com/somefolder/foo.txt. Звучит просто, не так ли? Ха.

Я попробовал класс Uri: Uri.TryCreate(new Uri("http://example.com/somefolder"), "foo.txt", out x);, в результате чего "http://example.com/foo.txt".

Затем я попробовал Path: System.IO.Path.Combine("http://example.com/somefolder", "foo.txt");, в результате получив "http://example.com/somefolder\foo.txt"... Ближе, но все же нет.

Для ударов я тогда попытался: System.IO.Path.Combine("http://example.com/somefolder/", "foo.txt"), в результате чего "http://example.com/somefolder/foo.txt".

Последняя работала, но в основном выполняла конкатенацию строк в этой точке.

Итак, у меня есть два варианта:

  • Использовать Path.Combine и заменить все \with/
  • Использование основной конкатенации строк

Мне не хватает встроенного метода framework для этого?

ОБНОВЛЕНИЕ:. Я использую для загрузки кучу файлов. Мой код выглядит следующим образом:

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

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

Мне посчастливилось использовать string concat в конструкторе Uri, а также проверить, нужна ли косая черта (которую я пропустил в коде).

Мне кажется, что то, что я пытаюсь сделать, будет очень много, так как класс Uri обрабатывает множество других протоколов, кроме http.

Ответы

Ответ 1

Flurl [раскрытие: я автор] - это крошечная библиотека построителей URL-адресов, которая может заполнить пробел с помощью метода Url.Combine:

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

Вы можете получить его через NuGet: Install-Package Flurl.

Я также хотел указать, что вы можете значительно повысить эффективность своего кода, загрузив файлы параллельно. Там есть пара способов сделать это. Если вы используете .NET 4.5 или выше и можете переписать downloadFile как метод async, то лучшим вариантом будет замена вашего цикла for на что-то вроде этого:

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

В противном случае, если вы застряли на .NET 4, вы можете легко достичь parallelism, используя Parallel.ForEach:

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

Ответ 2

Вот как работает класс Uri.

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");  

В основном делать это с помощью строковых манипуляций проще всего и делать

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

Если вы хотите узнать больше. Отметьте этот пост С# Url Builder Class

Обновленный ответ

Если ссылаться на базовый uri, он всегда будет http://example.com/ что-то справа - это просто путь.

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

Ответ 3

У меня есть статический метод для этой цели:

// 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;

}

Ответ 4

Вот версия LINQ, близкая к вышеуказанному ответу.

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

Ответ 5

Омг, почему все вы пишете такой сложный код? Это очень просто:

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

Пример использования:

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

Результат url - http://something.com/task/status/dfgd/111/qqq"