如何解析相对 URI?

10

如何从绝对 Uri 和相对 Uri 或相对路径获得指向相对位置的绝对 Uri

例如,假设我们有指向资源文件夹中位置的file:///android_asset/dirUri。进一步假设我们有一个相对路径/foo。那么该相对路径的绝对Uri应该是file:///android_asset/foo。在 Uri或Android SDK中是否有我忽略的提供这个结果的方法吗?

Uri.withAppendedPath() 不是答案,因为它似乎只处理尾部目录分隔符:

Uri abs=Uri.parse("file:///android_asset/");
Uri rel=Uri.withAppendedPath(abs, "/foo");
Assert.assertEquals("file:///android_asset/foo", rel.toString());
  // actually returns file:///android_asset//foo

Uri abs2=Uri.parse("file:///android_asset/dir");
Uri rel2=Uri.withAppendedPath(abs2, "/foo");
Assert.assertEquals("file:///android_asset/foo", rel2.toString());
  // actually returns file:///android_asset/dir//foo

Uri.Builder通过buildUpon()方法创建Uri对象,但并不是一个优化。

Uri rel3=abs.buildUpon().appendPath("/foo").build();
Assert.assertEquals("file:///android_asset/foo", rel3.toString());
  // actually returns file:///android_asset/%2Ffoo

Uri rel4=abs.buildUpon().appendEncodedPath("/foo").build();
Assert.assertEquals("file:///android_asset/foo", rel4.toString());
// actually returns file:///android_asset//foo
在紧急情况下,我可以尝试使用java.net.URL和它的URL(URL context, String spec)构造函数,或者自己编写一些代码,但我希望如果可能的话,仍然留在Android Uri值的领域内,只是为了区分URLUri之间的任何特殊性。

为什么不尝试使用Uri rel=Uri.withAppendedPath(abs, "foo");,然后返回值将是file:///android_asset/foo - BNK
@BNK:正如我在上一条评论中所写的,有许多可能的相对路径结构。/foo只是其中之一。还有 foo./foo../foo../../foo/bar 等等。许多人会使用绝对Uri的部分路径,而其他人(例如 /foo)则不会。如果可能的话,我会尽量处理所有情况。 - CommonsWare
@CommonsWare 这些代码怎么样?URI absolute = new URI("file:////android_asset/"); URI relative = new URI("foo/bar"); System.out.println(absolute.resolve(relative)); // I/System.out﹕ file://android_asset/foo/bar relative = new URI("./foo/bar"); System.out.println(absolute.resolve(relative)); // I/System.out﹕ file://android_asset/foo/bar relative = new URI("../foo"); System.out.println(absolute.resolve(relative)); // I/System.out﹕ file://foo - BNK
是的,如果绝对路径看起来像file:////android_asset/abc/xyz,那么没问题,我说得对吗? - BNK
1
@BNK:是的。基本上,file:///android_asset中的android_asset是“魔法”,必须以不同的方式对待,就像它是URL的主机一样。常规的file:///值没有这种限制。结果是,似乎SDK中没有处理此解析的功能,我将使用URI和/或Uri.Builder来拼凑一些东西。 - CommonsWare
显示剩余6条评论
2个回答

4
Android不容易做到这一点。
在我的情况下,我必须采取一个可能包含路径的基本URL:

http://www.myurl.com/myapi/

...并追加一个REST API方法路径,例如:

api/where/agencies-with-coverage.json

...生成完整的URL:

http://www.myurl.com/myapi/api/where/agencies-with-coverage.json

以下是我完成这个任务的方法(从应用程序中不同方法编译而来 - 可能有更简单的方法):
String baseUrlString = "http://www.myurl.com/myapi/";
String pathString = "api/where/agencies-with-coverage.json";
Uri.Builder builder = new Uri.Builder();
builder.path(pathString);

Uri baseUrl = Uri.parse(baseUrlString);

// Copy partial path (if one exists) from the base URL
Uri.Builder path = new Uri.Builder();
path.encodedPath(baseUrl.getEncodedPath());

// Then, tack on the rest of the REST API method path
path.appendEncodedPath(builder.build().getPath());

// Finally, overwrite builder with the full URL
builder.scheme(baseUrl.getScheme());
builder.encodedAuthority(baseUrl.getEncodedAuthority());
builder.encodedPath(path.build().getEncodedPath());

// Final Uri
Uri finalUri = builder.build();

在我的情况下,API客户端代码的构建器类在将pathbaseURL组合之前先组装了path,这解释了上面的顺序。
如果我正确地组合了以上代码,它应该处理URL字符串中的端口号和空格。
我从OneBusAway Android app中提取了这个源代码,具体来说是ObaContext class。请注意,Github上的此代码还处理了以下情况:用户键入了一个baseUrl(在上面的代码中为String serverName = Application.get().getCustomApiUrl()),该值应覆盖区域基本URL(mRegion.getObaBaseUrl()),并且用户输入的URL可能没有http://前缀。
上述代码在Github上通过的单元测试,包括在baseUrlpath中包含端口号和空格以及可能包含或不包含前导/尾随/的情况,链接在这里 on Github。相关问题在Github上,我一直在为了让这一切正常工作而苦苦挣扎 - 72, 126, 132
我还没有尝试过非HTTP URI,但我认为它可能更普遍适用。

并且,修复了另一个打字错误。 - Sean Barbeau
从技术上讲,这并没有回答问题。你所描述的是我在问题中所称的“只需为其编写一些代码”。根据这个和评论,我推断出我没有错过任何简单的SDK解决方案。因此,我会授予你赏金。感谢你的帮助! - CommonsWare
是的,这是我使用本机Android类所能达到的最接近的程度。我同意,考虑到复杂性,它确实属于“自己编写”的类别。感谢您的慷慨奖励! - Sean Barbeau

1

在Android中,URI.create(baseUrl).resolve(path)相当于Python中的urllib.parse.urljoin

import java.net.URI

URI.create("https://dmn92m25mtw4z.cloudfront.net/helpvids/f3_4/hls_480/480.m3u8")
    .resolve("0.ts")
// output:
// https://dmn92m25mtw4z.cloudfront.net/helpvids/f3_4/hls_480/0.ts

Sean Barbeau的回答返回了错误的URL,它只是将0.ts附加到URL后面。


1
谢谢,但请注意我所指的是Uri(Android特定类),而不是URIjava.net类)。它们可能是可互换的,但在没有更多测试的情况下,我不会假设它们是可互换的。 - CommonsWare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接