Get file size of an URL without download

时光总嘲笑我的痴心妄想 提交于 2020-06-13 06:37:41

问题


To get the file size of an URL without download, I wrote:

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();
    }

It seems to work on Simulator, Android and iOS with a testing URL of my Spring Boot server.

Anyway, I consider this code as a workaround, as I couldn't find an API that directly gives me the file size without starting the download first. Starting the download and then killing it works, but maybe there may be a better way to get the same result. By the way, the condition && evt.getLength() > 0 may never be satisfied in some cases (depending on the headers received), so it would be better to read only the headers, in which "Content-Length" may be present or absent.

So, my question is if, with Codename One, there is a way to download only the response headers, without starting the download. Thank you.


回答1:


Using the HTTP head request should give you the content length header that you can then use to get the size of the file without triggering a download. Your code might not follow through on the download but it does physically happen so a head request would be superior.

Unfortunately while there's a nice wrapper to head in Rest. This wrapper isn't very useful since there's no API to query response headers. That would make sense as an enhancement. You would need to derive ConnectionRequest and read the server response headers to get the content length.




回答2:


Thank you Shai, your answer https://stackoverflow.com/a/62124902/1277576 led me in the right direction. cr.setHttpMethod("HEAD"); simplifies the code and prevents the download from starting:

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();
    }

However, as you wrote, I can override ConnectionRequest for a more precise control of the headers. This other method performs the same function as the previous one, but it also guarantees me that the server supports partial downloads. In fact, if the server does not support partial downloads, the information about the content length would be useless for my purposes:

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

The readHeaders and getHeader methods are implementation dependent. I have verified that they work as desired on Simulator, Android and iOS.

Lastly, the Wrapper class is so implemented:

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

}

I hope this detailed answer will help those who need to read HTTP headers with Codename One.



来源:https://stackoverflow.com/questions/62121900/get-file-size-of-an-url-without-download

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!