HttpURLConnection - "https://"与"http://"的区别

10

我正在尝试获取用户输入的URL的网站图标,例如

_url = "google.com";

我使用 HttpUrlConnection 从主机 URL 的 /favicon.ico 扩展获取网站图标的位图。

        String faviconString = Uri.parse(_url).getHost() + "/favicon.ico";
        URL faviconUrl = null;
        Bitmap favicon = null;
        try
        {
            faviconString = "http://" + faviconString;
            faviconUrl = new URL(faviconString);
            HttpURLConnection connection = (HttpURLConnection) faviconUrl.openConnection();
            connection.setDoInput(true);
            connection.connect();
            favicon = BitmapFactory.decodeStream(connection.getInputStream());
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return favicon;

然而,由于用户可能不会指定http://https://,因此我必须自己添加。我遇到的问题是,如果在URL前面添加http://,一切都会正常工作,但对于https://,有些站点将返回favicon,而另一些站点则只会给我null。我如何找出哪个页面使用了https?我是否应该为每种情况都添加http://?是否有任何网站严格限制使用https并返回null以供使用http


1
这并不是一种非常可靠的技术,因为网页中可能会像这样定义favicon:<link rel="shortcut icon" href="/a/different/path.ico" type="image/x-icon" /> - Emiel
你有什么其他建议吗? WebIconDatabaseWebView.getFavicon() 是理想的选择,但是 WebIconDatabase 已经被弃用了。 - Jason Hu
提示:除了上述内容,一些网站可能会在http上返回null,并在https上工作。 - Sherif elKhatib
@JasonHu 你刚刚编辑了你的问题,说这基本上就是这个问题所涉及的!我指的是一个网站可能选择提供其中一种方案或两种方案。而我的评论早在你的编辑之前就已经发表了! - Sherif elKhatib
1
我说的原因是“我是否应该为每种情况都添加http://”?无论如何,硬编码这个Uri.parse(_url).getHost() + "/favicon.ico"非常不现实。但是,如果您坚持只获取此路径,可以进行HEAD请求以了解有关资源的详细信息,而无需获取数据。在此处检查:http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4 - Sherif elKhatib
显示剩余5条评论
5个回答

6
除非使用user2558882的想法或者现有其他工具可以直接获取网站的favicon,否则您需要检查http和https url。没有其他方法可以做到这一点。这是使用网络的难点之一。

也许从不同的角度审视您的代码,并将您尝试做的事情分解成更小的可管理部分会更好一些?

public void getFavicon(String host) {

    URL httpUrl = this.getHttpUrl(host + "/favicon.ico");

    Bitmap favicon = this.getBitmap(httpUrl);

    if (favicon == null) {

        URL httpsUrl = this.getHttpsUrl(host + "/favicon.ico");

        favicon = this.getBitmap(httpsUrl);
    }

    if (favicon == null) {

        throw new FaviconMissingException("Unable to find favicon for host: " + host);
    }

    return favicon;
}

public URL getHttpUrl(String uri) throws MalformedURLException {

    // There are better ways of building a url then string concationation.
    return new URL("http://" + uri);
}

public URL getHttpsUrl(String uri) throws MalformedURLException {

    // There are better ways of building a url then string concationation.
    return new URL("https://" + uri);
}

public Bitmap getBitmap(URL url) {

    InputStream inputStream = getInputStream(url);

    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

    return bitmap
}

public InputStream getInputStream(URL url) {

    // Please use a real connection library like HTTPClient here!
    // HttpClient will handle timeouts, redirects, and things like that for you.
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setDoInput(true);
    connection.connect();

    return connection.getInputStream();
}

顺便说一下,关注一两个连接所需的时间比编写代码以进行两个请求要多得多。我几乎可以保证谷歌会根据需要发出两个请求。如果对谷歌来说足够好,那对我来说也足够好。

最后,如果您开始发现进行两个请求真的需要太多时间,请采取措施改善性能。


4
注意:我不确定我的回答会有多大帮助。
您可以使用谷歌来获取网站的图标:
http://www.google.com/s2/favicons?domain=stackoverflow.com

返回:

图片描述

您无需指定httphttps

 http://www.google.com/s2/favicons?domain=my.yorku.ca ===>> (https://my.yorku.ca)

返回:

在此输入图片描述

但这不是https://my.yorku.ca实际使用的favicon。因此,我猜测谷歌会为没有提供访问其favicon的网站返回默认图标。

InputStream is = null;

String urlPrefix = "http://www.google.com/s2/favicons?domain=";

String _url = "google.com";

Bitmap favicon = null;

try {

    is = (InputStream) new URL(urlPrefix + _url).getContent();

} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

favicon = BitmapFactory.decodeStream(is);

您可以保留默认的网站图标并进行检查,具体操作如下:
if (defaultBitmap.sameAs(favicon)) {
    // favicon wasn't available
}

这是一个很酷的方法,但对于yorku示例,yorku.com/favicon.ico确实返回了正确的网站图标。因此,我可能仍然会选择/favicon.ico替代方案。 - Jason Hu
@JasonHu 只是想确保您使用的是有效的测试用例。yorku.com/favicon.ico 不是有效的,正确的域名应该是 http://yorku.ca。在我的答案中,我使用了 https://my.yorku.ca。当我尝试使用 https://my.yorku.ca/favicon.ico 时,得到了一个空值。对于 yorku.ca,谷歌的方法可以很好地工作。 - Vikram
我的第一条评论有点问题,我在我的测试中使用了 yorku.ca。我认为返回 null 是可以的,除非 /favicon.ico 返回了 null,而 google 返回的是不是默认图标的内容。当我收到 null 后,我也会在我的端口上使用默认图标。 - Jason Hu
@JasonHu 是的,这两种方法都适用于yorku.ca。但是谷歌的方法不适用于my.yorku.ca。你的方法适用吗? - Vikram
不行,它会返回“null”,在这种情况下我只会在我的页面上显示默认的网站图标。 - Jason Hu
@JasonHu 试试 _url = eff.org。谷歌的 URL 会返回网站图标,而 /favicon.ico 则会返回空值。所以我猜你应该找到一个情况:谷歌的方法失效了,而 /favicon.ico 的方法有效。 - Vikram

1
当URL以"https"开头时,请尝试以下操作:

              TrustManager[] trustAllCerts = new TrustManager[]
               {
                 new X509TrustManager()
                  {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers()  { return null; }
                    public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType)  {}
                    public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType)  {}
                  }
                 };
              try
                {
                  SSLContext sc = SSLContext.getInstance( "SSL"); // "TLS" "SSL"
                  sc.init( null, trustAllCerts, null);
                  HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory());
                  HttpsURLConnection.setDefaultHostnameVerifier( 
                   new HostnameVerifier() 
                    {
                      public boolean verify( String hostname, SSLSession session) { return true; }
                    } );
                }
               catch( Exception e)

1

另一个甚至更加“简单”的答案。

只需让用户输入包括协议在内的网站图标的URL,并验证该URL是否返回了网站图标。如果没有,则向最终用户显示验证错误。

遵循敏捷原则,尽量少做工作并查看结果。如果一个计划行不通,那就尝试其他方法。


1

如何检查网站返回 null 或者 favicon?

希望这能帮到你。


你的意思是先尝试 https:// 并检查返回的 favicon 是否为 null,然后再尝试 http:// 吗?这有点像暴力破解,如果可能的话,我宁愿不这样继续。毕竟这需要我打开两次连接,这并不理想。 - Jason Hu

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