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

问题描述 投票:1回答:2

为了获得没有下载的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,是否有一种方法可以仅下载响应标头,而无需开始下载。谢谢。

codenameone
2个回答
2
投票

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

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


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标头的人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.