【问题标题】:How to get exact size of zipped file before zipping?如何在压缩之前获得压缩文件的确切大小?
【发布时间】:2014-04-18 05:18:23
【问题描述】:

我正在使用以下独立类在压缩之前计算压缩文件的大小。 我正在使用 0 级压缩,但我仍然得到几个字节的差异。 你能帮我弄个确切的尺寸吗?

我们将不胜感激。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FilenameUtils;


public class zipcode {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub



         try {
             CRC32 crc = new CRC32();

                byte[] b = new byte[1024]; 
                File file = new File("/Users/Lab/Desktop/ABC.xlsx");
            FileInputStream in = new FileInputStream(file);
            crc.reset();
                // out put file 
                ZipOutputStream out = new ZipOutputStream(new FileOutputStream("/Users/Lab/Desktop/ABC.zip"));


                // name the file inside the zip  file 

                ZipEntry entry = new ZipEntry("ABC.xlsx");
                entry.setMethod(ZipEntry.DEFLATED);
                entry.setCompressedSize(file.length());
                entry.setSize(file.length());
                entry.setCrc(crc.getValue());
                out.setMethod(ZipOutputStream.DEFLATED);
                out.setLevel(0);
                //entry.setCompressedSize(in.available());
                //entry.setSize(in.available());
                //entry.setCrc(crc.getValue());


                out.putNextEntry(entry); 
                // buffer size

                int count;

                while ((count = in.read(b)) > 0) {
                    System.out.println();
                    out.write(b, 0, count);
                }
                out.close();
                in.close();         
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }

}

【问题讨论】:

  • 我不明白你在做什么。鉴于您必须完成大部分工作才能获得估算值,为什么不直接压缩文件?
  • 我必须将文件上传到服务器并即时压缩文件。但为此,我需要在上传之前获取确切的文件大小。我正在尝试的方式,我得到压缩文件和实际文件的几个字节差异。
  • 我需要在实际压缩之前获得确切的压缩文件大小。我不希望文件被压缩,但需要它们将它们存储在一个 zip 文件中。所以,我正在尝试 DEFLATED 和 STORED 选项。但是,我尝试的方式是,压缩文件和实际文件的字节差异很小。

标签: java zip


【解决方案1】:

首先,我不相信你为什么需要这样做的解释。如果需要在开始上传之前知道文件大小,则说明您的系统设计或实现存在问题。

话虽如此,解决方案基本上是在服务器端创建 ZIP 文件,以便在开始将其上传到客户端之前知道其大小:

  • 将 ZIP 文件写入临时文件并从中上传。

  • 将 ZIP 文件写入内存中的缓冲区并从中上传。

如果服务器端既没有文件空间也没有内存空间,那么:

  • 创建“接收器”输出流,它只计算写入的字节数以计算标称文件大小。

  • 创建/写入 ZIP 文件到接收器,并捕获文件大小。

  • 打开您的连接进行上传。

  • 发送包含文件大小的元数据。

  • 再次创建/写入 ZIP,写入套接字流 ... 或其他任何内容。

如果有帮助的话,这 3 种方法都可以让您创建和发送压缩的 ZIP。


如果您坚持尝试一次性完成此操作,那么您将需要详细阅读 ZIP 文件规范……并进行一些杂乱的算术运算。帮助您可能超出了 SO 问题的范围。

【讨论】:

    【解决方案2】:

    我必须自己这样做才能将 zip 结果直接写入需要文件大小的 AWS S3。不幸的是,如果不对每个数据块执行计算,我就无法计算压缩文件的大小。

    一种方法是将所有内容压缩两次。第一次扔掉数据但是把字节数加起来:

        long getSize(List<InputStream> files) throws IOException {
            final AtomicLong counter = new AtomicLong(0L);
            final OutputStream countingStream = new OutputStream() {
                @Override
                public void write(int b) throws IOException {
                    counter.incrementAndGet();
                }
            };
            ZipOutputStream zoutcounter = new ZipOutputStream(countingStream);
            // Loop through files or input streams here and do compression
            // ...
            zoutcounter.close();
                
            return counter.get();
        }
    
    

    另一种方法是执行上述操作,为每个文件创建一个条目,然后不写入任何实际数据(不要调用 write()),这样您就可以仅计算 zip 条目标题的总大小。这只有在您像这样关闭压缩时才有效:

    entry.setMethod(ZipEntry.STORED);
    

    zip 条目的大小加上每个未压缩文件的大小应该会为您提供准确的最终大小,但仅限于关闭压缩的情况下。在计算 zip 文件大小时,您不必设置 CRC 值或任何其他字段,因为这些条目在最终条目标题中始终具有相同的大小。只有 ZipEntry 上的 namecommentextra 字段大小不同。文件大小、CRC 等其他条目在最终 zip 文件中占用相同的空间,无论它们是否已设置。

    您还可以尝试另一种解决方案。保守地猜测大小并添加安全边际,然后积极压缩它。填充文件的其余部分,直到它等于您的估计大小。 Zip 忽略填充。如果您实现了一个输出流,它包装了您的实际输出流,但将关闭操作实现为 noop,那么您可以将其作为 ZipOutputStream 的输出流传递。关闭 ZipOutputStream 实例后,将填充写入实际输出流以等于您估计的字节数,然后真正关闭它。该文件将比它可能的大,但您保存了准确文件大小的计算,结果将受益于至少一些压缩。

    【讨论】:

      最近更新 更多