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

时间:2020-05-31 20:09:21

标签: codenameone

要获取未下载的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 Boot服务器的测试URL。

无论如何,我认为这段代码是一种解决方法,因为我找不到无需直接开始下载就无法直接提供文件大小的API。开始下载然后终止它可以正常工作,但是也许有更好的方法来获得相同的结果。顺便说一句,在某些情况下(取决于接收的标头)可能永远不会满足条件&& evt.getLength() > 0,因此最好只读取其中可能存在或不存在“ Content-Length”的标头。

所以,我的问题是,如果使用Codename One,是否有一种方法可以仅下载响应标头,而无需开始下载。谢谢。

2 个答案:

答案 0 :(得分:2)

使用HTTP标头请求应该为您提供内容长度标头,然后您可以使用该标头来获取文件的大小而无需触发下载。您的代码可能不会继续执行下载,但实际上确实会发生,因此,头请求会更好。

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

答案 1 :(得分:1)

感谢您,Shai,您的回答https://stackoverflow.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以更精确地控制标题。这另一种方法执行与前一种方法相同的功能,但也可以保证服务器支持部分下载。实际上,如果服务器不支持部分下载,则有关内容长度的信息对我而言将无用:

    /**
     * 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方法与实现有关。我已经验证它们可以在Simulator,Android和iOS上正常工作。

最后,Wrapper类是这样实现的:

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

}

我希望这个详细的答案对那些需要阅读具有Codename One的HTTP标头的人有帮助。