无需下载即可获取 URL 的文件大小

Get file size of an URL without download

要在不下载的情况下获取 URL 的文件大小,我写道:

public static long getFileSizeWithoutDownload(String url) {
        ConnectionRequest cr = new GZConnectionRequest();
        cr.setUrl(url);
        cr.setPost(false);
        NetworkManager.getInstance().addProgressListener((NetworkEvent evt) -> {
            if (cr == evt.getConnectionRequest() && evt.getLength() > 0) {
                cr.kill();
            }
        });
        NetworkManager.getInstance().addToQueueAndWait(cr);
        return cr.getContentLength();
    }

它似乎可以在模拟器、Android 和 iOS 上使用我的 Spring 引导服务器的测试 URL。

无论如何,我认为此代码是一种变通方法,因为我找不到 API 可以在不先开始下载的情况下直接为我提供文件大小。开始下载然后终止它是可行的,但也许有更好的方法来获得相同的结果。顺便说一句,条件&& evt.getLength() > 0在某些情况下可能永远不会满足(取决于接收到的headers),所以最好只读取headers,其中[=21] =] 可能存在或不存在。

所以,我的问题是,如果使用代号一,是否有一种方法可以只下载响应 headers,而无需开始下载。谢谢。

使用 HTTP head 请求应该会为您提供内容长度 header,然后您可以使用它来获取文件的大小而不会触发下载。您的代码可能无法完成下载,但它确实发生了,因此 head 请求会更好。

不幸的是,Rest 中有一个很好的 head 包装器。这个包装器不是很有用,因为没有 API 来查询响应 header。作为一种增强,这将是有意义的。您需要派生 ConnectionRequest 并读取服务器响应 headers 以获得内容长度。

谢谢 Shai,你的回答 https://whosebug.com/a/62124902/1277576 引导我朝着正确的方向前进。 cr.setHttpMethod("HEAD"); 简化代码并防止下载开始:

public static long getFileSizeWithoutDownload(String url) {
        ConnectionRequest cr = new GZConnectionRequest();
        cr.setUrl(url);
        cr.setHttpMethod("HEAD");
        cr.setPost(false);
        NetworkManager.getInstance().addToQueueAndWait(cr);
        return cr.getContentLength();
    }

但是,正如您所写,我可以覆盖 ConnectionRequest 以更精确地控制 headers。另一种方法与前一种方法执行相同的功能,但它也向我保证服务器支持部分下载。事实上,如果服务器不支持部分下载,关于内容长度的信息对我来说就没有用了:

    /**
     * Returns -2 if the server doesn't accept partial downloads, -1 if the
     * content length is unknow, a value greater than 0 if the Content-Length is
     * known
     *
     * @param url
     * @return must be interpreted as a boolean value: if greater than zero than
     * partial downloads are supported (the returned value is the Content-Length),
     * otherwise they are not supported.
     */
    public static long getFileSizeWithoutDownload(String url) {
        // documentation about the headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
        Wrapper<Long> result = new Wrapper<>(0l);
        ConnectionRequest cr = new GZConnectionRequest() {
            @Override
            protected void readHeaders(Object connection) throws IOException {
                String acceptRanges = getHeader(connection, "Accept-Ranges");
                if (acceptRanges == null || !acceptRanges.equals("bytes")) {
                    Log.p("The partial downloads of " + url + " are not supported.", Log.WARNING);
                    result.set(-2l);
                } else {
                    String contentLength = getHeader(connection, "Content-Length");
                    if (contentLength != null) {
                        result.set(Long.parseLong(contentLength));
                    } else {
                        Log.p("The Content-Length of " + url + " is unknown.", Log.WARNING);
                        result.set(-1l);
                    }
                }
            }
        };
        cr.setUrl(url);
        cr.setHttpMethod("HEAD");
        cr.setPost(false);
        NetworkManager.getInstance().addToQueueAndWait(cr);
        return result.get();
    }

readHeadersgetHeader 方法依赖于实现。我已经验证它们在模拟器 Android 和 iOS.

上可以正常工作

最后,Wrapperclass是这样实现的:

/**
 * Generic object wrapper, as workaround for the issue "Local variables
 * referenced from a lambda expression must be final or effectively final".
 */
public class Wrapper<T> {

    private T object;

    public Wrapper(T obj) {
        this.object = obj;
    }

    public T get() {
        return object;
    }

    public void set(T obj) {
        this.object = obj;
    }

}

我希望这个详细的回答能帮助那些需要阅读 HTTP headers with Codename One 的人。