【问题标题】:Get size of uncompressed gzip file while size of compressed file is available from server获取未压缩 gzip 文件的大小,而压缩文件的大小可从服务器获得
【发布时间】:2013-03-05 03:09:00
【问题描述】:

我正在使用GZIPInputStream 下载 PDF 文件。我想在 UI 按钮上显示文件的下载进度。但是,我没有得到文件的实际大小,我得到的是压缩大小,因此我无法显示正确的下载进度。此下载进度超过100,因为实际文件大小大于文件压缩大小。

来自服务器的文件的标头内容:我从服务器接收到以下信息,我正在使用content-length 提供压缩文件大小。

1.Connection
2.Content-Encoding
3.Content-length
4.Content-Type
5.Keep-Alive
6.Server
7.Date

这是我的代码。有什么办法可以得到文件的原始大小?

long fileLength =  httpResponse.getEntity().getContentLength();//
GZIPInputStream input = new GZIPInputStream(new BufferedInputStream(
        httpResponse.getEntity().getContent()));
FileOutputStream output = new FileOutputStream(destinationFilePath);

byte data[] = new byte[1024];
long total = 0;
float percentage = 0;
int count;
currentDownloadingPercentage=0;
while ((count = input.read(data)) != -1) {
    total += count;
    output.write(data, 0, count);

    // publishing the progress....
    percentage = (float)total/(float)fileLength;
    percentage *= 100;
    if ((int)percentage > (int)currentDownloadingPercentage) {
        currentDownloadingPercentage = percentage;
        Bundle resultData = new Bundle();
        resultData.putBoolean(DOWNLOAD_FAILED, false);
        resultData.putInt(DOWNLOAD_PROGRESS ,(int)percentage);
        receiver.send(processID, resultData);
        resultData = null;  
    }
}

【问题讨论】:

    标签: java android gzip


    【解决方案1】:

    你看错了。您应该计算您读取的压缩字节并根据这些计算进度。相反,您正在计算解压缩的字节并将其与压缩文件的大小进行比较。在回答您的问题时,没有(可靠的)方法可以在不解压缩的情况下确定 gzip 压缩文件的大小。

    更新:这是计算传入的未压缩字节的一种方法。在用 GZIPInputStream 包装原始输入流之前,先用 TeeInputStream 包装原始输入流。将 TeeInputStream 分支设为CountingOutputStream。然后您将始终获得通过getByteCount()下载的压缩字节的当前计数

    【讨论】:

    • 我也尝试过使用 InputStream,这在进度部分工作正常,但流导致压缩文件。在脱机模式下解压缩此文件比平时花费更多。为文件逐字节解压缩又花了整整一轮。下面是只读取压缩字节的代码..InputStream input = new BufferedInputStream(httpResponse.getEntity().getContent());
    • 我已经更新了我的答案,用一种即时计算压缩字节数的方法。
    • Ryan,我应该如何在 android 中做到这一点? android 中 TeeInputStream 的任何替代方案?
    • 哦,我没注意到您使用的是 Android。自己编写并不难,commons-io 是开源的,所以你可以从那里获取源文件。
    【解决方案2】:

    这个issue的讨论结果似乎无法避免HttpURLConnection.getInputStream()自动返回GZIPInputStream,一旦你让HttpURLConnection接受gzip压缩,你将无法准确计算下载进度,我们唯一能做的就是禁用gzip作为可接受的编码:

    HttpURLConnection.setRequestProperty("Accept-Encoding", "identity");
    

    另一种选择是使用AndroidHttpClient,我已经对此进行了测试,即使我们现在也接受这样的gzip编码:

    HttpUriRequest.addHeader("Accept-Encoding", "gzip");
    

    HttpResponse.getEntity().getContent() 返回的 InputStream 实例将是 EofSensorInputStream,原来的 InputStream 是我们想要的,不是 GZIPInputStream,让我们可以自己封装到 GZIPInputStream 中,我们可以使用 TeeInputStream 和 CountingOutputStream 来完成下载进度的计算。

    HttpResponse response = ...;
    HttpEntity entity = response.getEntity();
    long fileSize = entity.getContentLength();
    
    InputStream ins = entity.getContent(); // instance of EofSensorInputStream
    CountingOutputStream coStrem = new CountingOutputStream(new ByteArrayOutputStream(100));
    GZIPInputStream inStrem = new GZIPInputStream(new TeeInputStream(ins, coStrem, true));
    
    byte[] buffer = new byte[6 * 1024]; // 6K buffer
    int offset;
    
    while ((offset = inStrem.read(buffer)) != -1) {
        tmpFileRaf.write(buffer, 0, offset);
        postDownloadProgress(fileSize, coStrem.getByteCount());
    }
    

    我认为这就是我们可以解决这个问题的全部,我尝试在我的项目中选择 android libcore 源,以便我们可以自定义 HttpURLConnectionImpl 然后抑制它返回 GZIPInputStream,但是很多错误都会造成麻烦,我放弃这项努力。

    post 中,Jesse Wilson 建议我们最好的 Android 客户端是 HttpURLConnection,所以我一直在寻找如何解决这个问题,希望我能尽快找到方法。

    【讨论】: