如何从HttpURLConnection获取gzip压缩数据的大小

时间:2014-10-10 15:39:13

标签: java

我试图获取从URLConnection获取的数据长度。

由于Im测量传输的数据量,我不想知道未压缩数据的大小,而是压缩数据的大小。不幸的是,InputStream会自动解压缩gzip压缩数据。

我必须手动下载整个文件,以防输出被分块并且我无法通过connection.getContentLength()获取长度;

代码在这里

try {
    connection = (HttpURLConnection) (new URL(url)).openConnection();
    connection.connect();
    int contentLength = connection.getContentLength();

    if (contentLength == -1 && connection != null) {
        InputStream input = connection.getInputStream();

        byte[] buffer = new byte[4096];
        int count = 0, len;
        while ((len = input.read(buffer)) > 0) {
            count += len;
        }

        contentLength = count;
    }

    totalSize += contentLength;
}

您可以看到此文件的示例:http://www.google-analytics.com/analytics.js 当我在Chrome中检查标题时,它会显示Content-Length:11181。但是我无法通过URLConnection获取此内容长度(它返回-1),因此我尝试下载该文件。但是,我的输出是25421字节,这是未压缩文件的大小。

感谢您提供任何帮助。

1 个答案:

答案 0 :(得分:3)

您必须将Accept-Encoding标头设置为“ gzip,deflate”,以使服务器知道您的客户端接受压缩数据。

String url = "https://www.google-analytics.com/analytics.js";
HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
connection.connect();
int contentLength = connection.getContentLength();
System.out.println("Content-Length: " + contentLength);

没有此标头,您将强制网站返回纯文本数据。如果数据太大,则站点可能会分块返回响应,在这种情况下,响应将没有Content-Length标头。

来自developer.mozilla, Transfer-Encoding, chunked

  

数据按一系列块发送。在这种情况下,将省略Content-Length标头,并且在每个块的开头,您需要以十六进制格式添加当前块的长度,后跟'\ r \ n',然后是块本身,然后是另一个'\ r \ n'。终止块是常规块,但其长度为零。紧随其后的是预告片,该预告片由一个(可能为空)实体标题字段序列组成。

如果响应分块,恐怕您必须读取所有数据才能知道其大小。每个块前面都带有一个十六进制数字,该数字指示块的大小。我想您可以使用此数字来计算总数据大小,但是您仍然必须读取所有数据,因此这样做没有任何好处。我们可以检查是否使用Transfer-Encoding标头对响应进行了分块。

String url = "https://www.google-analytics.com/analytics.js";
HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
connection.connect();
String transferEncoding = connection.getHeaderField("Transfer-Encoding");
System.out.println("Transfer-Encoding: " + transferEncoding);

在这种情况下,您必须将原始响应数据存储在字节数组中,以便找到压缩数据的大小。

InputStream input = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int n;
while ((n = input.read(buffer)) > 0) {
    baos.write(buffer, 0, n);
}
byte[] zippedData = baos.toByteArray();
System.out.println(zippedData.length);

因此,我想出了一个“ hack”,它可能会揭示分块响应的数据大小,而不读取它。如果我们使用Range标头,则服务器可能会响应一个Content-Range标头。此标头将包含发送的字节和内容的总字节。请注意,这不是检测内容大小的可靠方法,如果服务器不支持范围请求,则此方法将无效。

String url = "https://www.google-analytics.com/analytics.js";        
HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
connection.setRequestProperty("Range", "bytes=0-1");
connection.connect();

int contentLength = connection.getContentLength();
String contentRange = connection.getHeaderField("Content-Range");
if (contentRange != null) {
    contentLength = Integer.parseInt(contentRange.split("/")[1]);
}
System.out.println("Content-Length: " + contentLength);