为了获得没有下载的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,是否有一种方法可以仅下载响应标头,而无需开始下载。谢谢。
使用HTTP标头请求应为您提供内容长度标头,然后您可以使用该标头来获取文件的大小而无需触发下载。您的代码可能不会继续进行下载,但实际上确实会发生,因此,头请求可能会更好。
[很遗憾,head
中有一个不错的Rest
包装器。这个包装器不是很有用,因为没有API可以查询响应标头。作为增强,这将是有意义的。您将需要派生ConnectionRequest
并读取服务器响应标头以获取内容长度。
[谢谢你,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();
}
readHeaders
和getHeader
方法取决于实现。我已经验证它们可以在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标头的人有所帮助。