在Java中有效地读取zip文件

时间:2012-05-24 15:07:33

标签: java performance zip nio large-data

我正在处理一个处理大量数据的项目。 我有很多(数千)zip文件,每个文件包含一个简单的txt文件,包含数千行(大约80k行)。 我目前正在做的是:

for(File zipFile: dir.listFiles()){
ZipFile zf = new ZipFile(zipFile);
ZipEntry ze = (ZipEntry) zf.entries().nextElement();
BufferedReader in = new BufferedReader(new InputStreamReader(zf.getInputStream(ze)));
...

通过这种方式,我可以逐行读取文件,但它的定义太慢了。 鉴于需要读取大量文件和行,我需要以更有效的方式阅读它们。

我找了一种不同的方法,但我找不到任何东西。 我认为我应该使用的是用于密集I / O操作的java nio API,但我不知道如何将它们与zip文件一起使用。

真的很感激任何帮助。

谢谢,

6 个答案:

答案 0 :(得分:3)

  

我有很多(数千个)zip文件。压缩文件各约30MB,而zip文件中的txt约为60/70 MB。使用此代码读取和处理文件需要大约15个小时,但这取决于。

让我们做一些背后的计算。

假设您有5000个文件。如果处理它们需要15个小时,这相当于每个文件约10秒。这些文件大约每个30MB,因此吞吐量约为3MB / s。

这比ZipFile可以解压缩的速度慢一到两个数量级。

磁盘是否有问题(它们是本地磁盘还是网络共享?),或者是大部分时间都在进行的实际处理。

确定最佳方法是使用分析器。

答案 1 :(得分:1)

迭代zip文件的正确方法

final ZipFile file = new ZipFile( FILE_NAME );
try
{
    final Enumeration<? extends ZipEntry> entries = file.entries();
    while ( entries.hasMoreElements() )
    {
        final ZipEntry entry = entries.nextElement();
        System.out.println( entry.getName() );
        //use entry input stream:
        readInputStream( file.getInputStream( entry ) )
    }
}
finally
{
    file.close();
}

private static int readInputStream( final InputStream is ) throws IOException {
    final byte[] buf = new byte[ 8192 ];
    int read = 0;
    int cntRead;
    while ( ( cntRead = is.read( buf, 0, buf.length ) ) >=0  )
    {
        read += cntRead;
    }
    return read;
}

Zip文件由几个条目组成,每个条目都有一个包含当前条目中字节数的字段。因此,很容易迭代所有zip文件条目而无需实际的数据解压缩。 java.util.zip.ZipFile接受文件/文件名,并使用随机访问在文件位置之间跳转。另一方面,java.util.zip.ZipInputStream正在使用流,因此它无法自由跳转。这就是为什么它必须读取和解压缩所有zip数据才能达到每个条目的EOF并读取下一个条目标题。

这是什么意思?如果您的文件系统中已有zip文件 - 无论您的任务如何,都使用ZipFile进行处理。作为奖励,您可以按顺序或随机访问zip条目(性能损失相当小)。另一方面,如果您正在处理流,则需要使用ZipInputStream按顺序处理所有条目。

这是一个例子。包含三个0.6Gb条目的zip存档(总文件大小= 1.6Gb)使用ZipFile在0.05秒内迭代,使用ZipInputStream在18秒内迭代。

答案 2 :(得分:0)

您可以像这样使用新文件API:

Path jarPath = Paths.get(...);
try (FileSystem jarFS = FileSystems.newFileSystem(jarPath, null)) {
    Path someFileInJarPath = jarFS.getPath("/...");
    try (ReadableByteChannel rbc = Files.newByteChannel(someFileInJarPath, EnumSet.of(StandardOpenOption.READ))) {
        // read file
    }
}

代码适用于jar文件,但我认为它也适用于拉链。

答案 3 :(得分:0)

您可以尝试此代码

try
    {

        final ZipFile zf = new ZipFile("C:/Documents and Settings/satheesh/Desktop/POTL.Zip");

        final Enumeration<? extends ZipEntry> entries = zf.entries();
        ZipInputStream zipInput = null;

        while (entries.hasMoreElements())
        {
            final ZipEntry zipEntry=entries.nextElement();
            final String fileName = zipEntry.getName();
        // zipInput = new ZipInputStream(new FileInputStream(fileName));
            InputStream inputs=zf.getInputStream(zipEntry);
            //  final RandomAccessFile br = new RandomAccessFile(fileName, "r");
                BufferedReader br = new BufferedReader(new InputStreamReader(inputs, "UTF-8"));
                FileWriter fr=new FileWriter(f2);
            BufferedWriter wr=new BufferedWriter(new FileWriter(f2) );

            while((line = br.readLine()) != null)
            {
                wr.write(line);
                System.out.println(line);
                wr.newLine();
                wr.flush();
            }
            br.close();
            zipInput.closeEntry();
        }


    }
    catch(Exception e)
    {
        System.out.print(e);
    }
    finally
    {
        System.out.println("\n\n\nThe had been extracted successfully");

    }

这段代码工作得很好。

答案 4 :(得分:0)

英特尔已经改进了zlib版本,Java使用内部peroform zip / unzip。它要求您使用Interl IPP paches修补zlib源。 我做了一个benchmark,吞吐量增加了1.4倍到3倍。

答案 5 :(得分:0)

异步拆包和同步处理

使用Java Performance的建议,就像answer from Wasim Wanithat from Satheesh Kumar一样:对ZIP条目进行迭代以获取每个InputStream的内容,并且关于他们的一些事情,我建立了自己的解决方案。

在我的情况下,处理是瓶颈,因此我从一开始就大规模启动并行提取,在entries.hasMoreElements(),上进行迭代,并将每个结果放入我从处理中消耗的ConcurrentLinkedQueue中线。我的ZIP包含一组表示序列化Java对象的XML文件,因此我的“提取”包括反序列化这些对象,而那些反序列化的对象就是放在队列中的那些对象。

对我来说,与我以前从ZIP顺序获取每个文件并对其进行处理的方法相比,它具有一些优势:

  1. 最引人注目的之一:总时间减少了10%
  2. 文件发布较早
  3. 整个RAM的分配速度更快,因此,如果没有足够的RAM,它将更快地失败(在几十分钟而不是一小时以上的时间内);请注意,处理后我保留分配的内存量与解压缩后的文件占用的内存量非常相似,否则,最好依次解压缩并丢弃以保持较低的内存占用量
  4. 解压缩和反序列化似乎占用了很高的CPU使用率,因此完成得越快,您获取CPU进行处理的速度就越快,这确实很重要

有一个缺点:包括并行性时,流控制要稍微复杂一些。