无需下载即可获取 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();
}
readHeaders
和 getHeader
方法依赖于实现。我已经验证它们在模拟器 Android 和 iOS.
上可以正常工作
最后,Wrapper
class是这样实现的:
/**
* 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 的人。
要在不下载的情况下获取 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();
}
readHeaders
和 getHeader
方法依赖于实现。我已经验证它们在模拟器 Android 和 iOS.
最后,Wrapper
class是这样实现的:
/**
* 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 的人。