如何在Java中从URL计算文件大小

25

我正在尝试从一个Web服务获取一堆pdf链接,并且我想给用户每个链接的文件大小。

有没有办法完成这个任务?

谢谢


恐怕您必须至少下载一次文件才能获得准确的大小。(您可能想要将其存储以备将来之用,但如果服务器上的文件发生更改,则数据将过时) - Nishant
1
@Nishant 这不是真的。一个 HTTP HEAD 请求返回有关执行 HTTP GET 请求时将获得什么信息,其中应包括返回请求的大小的信息。您可以执行 HEAD 请求并解析响应。 - Vala
1
content-length 是可选项,可能不存在。 - njzk2
7个回答

46
使用 HEAD 请求,您可以像这样做:
private static int getFileSize(URL url) {
    URLConnection conn = null;
    try {
        conn = url.openConnection();
        if(conn instanceof HttpURLConnection) {
            ((HttpURLConnection)conn).setRequestMethod("HEAD");
        }
        conn.getInputStream();
        return conn.getContentLength();
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        if(conn instanceof HttpURLConnection) {
            ((HttpURLConnection)conn).disconnect();
        }
    }
}

我收到了以下异常:java.io.IOException: 服务器返回HTTP响应代码:405,URL。我可以在浏览器中打开PDF链接。这是否意味着该URL不允许头请求? - javaMan
1
你能分享一下你的请求 URL 吗? - user1723178
很抱歉,URL需要我的凭据。我不能分享它。:( - javaMan
1
如果状态码是405,使用“GET”而不是“HEAD”。 - irreputable
2
当我以调试模式运行并查看 conn 对象时,它返回 -1。我发现许多字段都为 null、false 或 -1。 - javaMan
如果无法打开URL,则在conn.disconnect()处引发NPE。 - Michel Jung

19

被接受的答案容易出现NullPointerException,对于大于2GiB的文件不起作用,并且包含一个不必要的getInputStream()调用。这里是修正后的代码:

public long getFileSize(URL url) {
  HttpURLConnection conn = null;
  try {
    conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("HEAD");
    return conn.getContentLengthLong();
  } catch (IOException e) {
    throw new RuntimeException(e);
  } finally {
    if (conn != null) {
      conn.disconnect();
    }
  }
}

更新:已经有更新,但仍存在问题。


10

尝试使用HTTP HEAD方法,它只返回HTTP头信息。头部标签Content-Length应包含你需要的信息。


4

你是否尝试过在URL连接上使用getContentLength?如果服务器返回有效的头文件,你应该能够获取文档的大小。

但请注意,Web服务器也可能以块的形式返回文件。在这种情况下,我IRC的内容长度方法将返回一个块的大小(≤1.4)或-1(>1.4)。


3

如果您使用的是Android系统,这里有一个Java解决方案:

最初的回答:

/**@return the file size of the given file url , or -1L if there was any kind of error while doing so*/
@WorkerThread
public static long getUrlFileLength(String url) {
    try {
        final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
        urlConnection.setRequestMethod("HEAD");
        final String lengthHeaderField = urlConnection.getHeaderField("content-length");
        Long result = lengthHeaderField == null ? null : Long.parseLong(lengthHeaderField);
        return result == null || result < 0L ? -1L : result;
    } catch (Exception ignored) {
    }
    return -1L;
}

最初的回答
而在Kotlin中:
/**@return the file size of the given file url , or -1L if there was any kind of error while doing so*/
@WorkerThread
fun getUrlFileLength(url: String): Long {
    return try {
        val urlConnection = URL(url).openConnection() as HttpURLConnection
        urlConnection.requestMethod = "HEAD"
        urlConnection.getHeaderField("content-length")?.toLongOrNull()?.coerceAtLeast(-1L)
                ?: -1L
    } catch (ignored: Exception) {
        -1L
    }
}

如果您的应用程序来自Android N,则可以使用以下内容代替:"最初的回答"
/**@return the file size of the given file url , or -1L if there was any kind of error while doing so*/
@WorkerThread
fun getUrlFileLength(url: String): Long {
    return try {
        val urlConnection = URL(url).openConnection() as HttpURLConnection
        urlConnection.requestMethod = "HEAD"
        urlConnection.contentLengthLong.coerceAtLeast(-1L)
    } catch (ignored: Exception) {
        -1L
    }
}

我已经尝试了所有可能的方法,但它总是返回-1。此外,附上的是标题数据: - golchha21
{null=[HTTP/1.1 200 OK], Accept-Ranges=[bytes], Cache-Control=[max-age=2592000], Connection=[keep-alive, Keep-Alive], Content-Type=[application/vnd.android.package-archive], Date=[Thu, 25 Jul 2019 14:01:44 GMT], ETag=["3f416f-58e6adf532900-gzip"], Expires=[Sat, 24 Aug 2019 14:01:44 GMT], Keep-Alive=[timeout=3, max=75], Last-Modified=[Wed, 24 Jul 2019 10:35:48 GMT], Server=[Apache/2.4.39 (cPanel) OpenSSL/1.0.2r mod_bwlimited/1.4 Phusion_Passenger/5.3.7], Transfer-Encoding=[chunked]} - golchha21
升级=[h2,h2c],变量=[Accept-Encoding,User-Agent],X-Android-Received-Millis=[1564063303579],X-Android-Response-Source=[NETWORK 200],X-Android-Selected-Protocol=[http/1.1],X-Android-Sent-Millis=[1564063302765]} - golchha21
这是当我使用urlConnection.headerFields时返回的数据。 - golchha21
我理解这一点,但是当我通过浏览器使用另一个工具检查头数据时,Content-Length在那里是可见的。 - golchha21
显示剩余3条评论

3
HTTP响应头包含Content-Length,因此您可以查询URLConnection对象以获取此值。
打开URL连接后,您可以尝试以下操作:
List values = urlConnection.getHeaderFields().get("content-Length")
if (values != null && !values.isEmpty()) {

    // getHeaderFields() returns a Map with key=(String) header 
    // name, value = List of String values for that header field. 
    // just use the first value here.
    String sLength = (String) values.get(0);

    if (sLength != null) {
       //parse the length into an integer...
       ...
    }
}

0

你可以尝试这个...

private long getContentLength(HttpURLConnection conn) {
    String transferEncoding = conn.getHeaderField("Transfer-Encoding");
    if (transferEncoding == null || transferEncoding.equalsIgnoreCase("chunked")) {
        return conn.getHeaderFieldInt("Content-Length", -1);
    } else {
        return -1;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接