从Zip文件中读取包含的文件内容

93

我正在尝试创建一个简单的Java程序,它可以读取并从zip文件中的文件(txt、pdf、docx)中提取内容。我需要读取所有这些文件的内容,并且我正在使用Apache Tika来实现此目的。

有人可以帮我实现这个功能吗?我已经尝试了一些方法,但没有成功。

代码片段:

public class SampleZipExtract {


    public static void main(String[] args) {

        List<String> tempString = new ArrayList<String>();
        StringBuffer sbf = new StringBuffer();

        File file = new File("C:\\Users\\xxx\\Desktop\\abc.zip");
        InputStream input;
        try {

          input = new FileInputStream(file);
          ZipInputStream zip = new ZipInputStream(input);
          ZipEntry entry = zip.getNextEntry();

          BodyContentHandler textHandler = new BodyContentHandler();
          Metadata metadata = new Metadata();

          Parser parser = new AutoDetectParser();

          while (entry!= null){

                if(entry.getName().endsWith(".txt") || 
                           entry.getName().endsWith(".pdf")||
                           entry.getName().endsWith(".docx")){
              System.out.println("entry=" + entry.getName() + " " + entry.getSize());
                     parser.parse(input, textHandler, metadata, new ParseContext());
                     tempString.add(textHandler.toString());
                }
           }
           zip.close();
           input.close();

           for (String text : tempString) {
           System.out.println("Apache Tika - Converted input string : " + text);
           sbf.append(text);
           System.out.println("Final text from all the three files " + sbf.toString());
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TikaException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

1
为什么不直接将zip文件传递给Apache Tika呢?它会调用您提供的递归解析器来处理zip中的每个文件,因此您无需进行任何特殊操作! - Gagravarr
1
这正是我想知道的,但是我找不到足够的教程来学习如何做到这一点。我也有点担心这个问题 - http://www.javamex.com/tutorials/compression/zip_problems.shtml,不确定Tika是否解决了这个问题。 - S Jagdeesh
Tika使用commons compress来解决许多这些问题。 - Gagravarr
3
Tika需要61兆字节的空间?仅用于处理ZIP文件,这可以通过大约10个字符串来完成!我的应用程序有15多个活动页面,重量不到4兆字节。我认为,仅为琐碎的任务而使应用程序变得如此庞大是对用户的不尊重。 - Acuna
6个回答

226

如果你想知道如何从每个ZipEntry获取文件内容,实际上非常简单。以下是一段示例代码:

public static void main(String[] args) throws IOException {
    ZipFile zipFile = new ZipFile("C:/test.zip");

    Enumeration<? extends ZipEntry> entries = zipFile.entries();

    while(entries.hasMoreElements()){
        ZipEntry entry = entries.nextElement();
        InputStream stream = zipFile.getInputStream(entry);
    }
}

一旦你获得了InputStream,你就可以按照自己的意愿进行阅读。


31
别忘了关闭inputStream和ZipFile以避免资源泄漏 :)。 - Noremac
3
zipFile.entries();这个类型没有定义entries函数。 - Vasanth Nag K V
2
有没有办法将byte[]数组传递给ZipFile(content.getBytes())的构造函数?如果没有,我们该怎么做? - Simple-Solution
1
@Simple-Solution 我认为最简单的方法是将字节数组写入一个新的“文件”,并将该“文件”实例提供给构造函数。 - Rodrigo Sasaki
终极解决方案 +1 - Onic Team

61

从Java 7开始,NIO API提供了一种更好、更通用的访问ZIP或JAR文件内容的方式。实际上,它现在是一个统一的API,允许您像处理普通文件一样处理ZIP文件。

为了在此API中提取ZIP文件中包含的所有文件,您可以按如下所示操作。

在Java 8中

private void extractAll(URI fromZip, Path toDirectory) throws IOException {
    FileSystems.newFileSystem(fromZip, Collections.emptyMap())
            .getRootDirectories()
            .forEach(root -> {
                // in a full implementation, you'd have to
                // handle directories 
                Files.walk(root).forEach(path -> Files.copy(path, toDirectory));
            });
}

在Java 7中

private void extractAll(URI fromZip, Path toDirectory) throws IOException {
    FileSystem zipFs = FileSystems.newFileSystem(fromZip, Collections.emptyMap());

    for (Path root : zipFs.getRootDirectories()) {
        Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
                    throws IOException {
                // You can do anything you want with the path here
                Files.copy(file, toDirectory);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) 
                    throws IOException {
                // In a full implementation, you'd need to create each 
                // sub-directory of the destination directory before 
                // copying files into it
                return super.preVisitDirectory(dir, attrs);
            }
        });
    }
}

13
这既令人惊叹又疯狂。 - Esko
3
操作完成后应关闭FileSystem。 - Ahmed Ashour
9
在 Java 8 版本中,Files.walk(root) 抛出的 IOException 无法通过 lambda 表达式传播。 - barteks2x
使用try-with-resources! - Florens

11
由于 while 中的条件,循环可能永远不会终止。
while (entry != null) {
  // If entry never becomes null here, loop will never break.
}

不要在那里检查 null,你可以尝试这样做:

ZipEntry entry = null;
while ((entry = zip.getNextEntry()) != null) {
  // Rest of your code
}

我们不能只使用 while (zip.getNextEntry() != null) 吗? - Shatir
@Shatir 希望你已经尝试过并认识到在while块内部不会有对ZipEntry的引用。如果你愿意,这段代码也可以起作用:ZipEntry entry = zip.getNextEntry(); while (entry !=null) { /* do stuff */ entry = zip.getNextEntry(); } - CodeShane

3

以下是您可以使用的示例代码,让Tika代替您处理容器文件。 http://wiki.apache.org/tika/RecursiveMetadata

就我所知,被接受的解决方案在存在嵌套的zip文件时不起作用。然而,Tika 可以处理这种情况。


2

我实现这个的方法是创建一个ZipInputStream包装类,它将处理并提供当前条目的流:

这个包装器类:

public class ZippedFileInputStream extends InputStream {

    private ZipInputStream is;

    public ZippedFileInputStream(ZipInputStream is){
        this.is = is;
    }

    @Override
    public int read() throws IOException {
        return is.read();
    }

    @Override
    public void close() throws IOException {
        is.closeEntry();
    }

关于 IT 的使用:

    ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream("SomeFile.zip"));

    while((entry = zipInputStream.getNextEntry())!= null) {

     ZippedFileInputStream archivedFileInputStream = new ZippedFileInputStream(zipInputStream);

     //... perform whatever logic you want here with ZippedFileInputStream 

     // note that this will only close the current entry stream and not the ZipInputStream
     archivedFileInputStream.close();

    }
    zipInputStream.close();

这种方法的一个优点是:将InputStream作为参数传递给处理它们的方法,这些方法往往在完成后立即关闭输入流。


0

我是这样做的,记得更改URL或ZIP文件 JDK 15

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.io.*;
import java.util.*;
import java.nio.file.Paths;

class Main {
  public static void main(String[] args) throws MalformedURLException,FileNotFoundException,IOException{
    String url,kfile;
    Scanner getkw = new Scanner(System.in);
    System.out.println(" Please Paste Url ::");
    url = getkw.nextLine();
    System.out.println("Please enter name of file you want to save as :: ");
    kfile = getkw.nextLine();
    getkw.close();
    Main Dinit = new Main();
    System.out.println(Dinit.dloader(url, kfile));
    ZipFile Vanilla = new ZipFile(new File("Vanilla.zip"));
    Enumeration<? extends ZipEntry> entries = Vanilla.entries();

    while(entries.hasMoreElements()){
        ZipEntry entry = entries.nextElement();
//        String nextr =  entries.nextElement();
        InputStream stream = Vanilla.getInputStream(entry);
        FileInputStream inpure= new FileInputStream("Vanilla.zip");
        FileOutputStream outter = new FileOutputStream(new File(entry.toString()));
        outter.write(inpure.readAllBytes());
        outter.close();
    }

  }
  private String dloader(String kurl, String fname)throws IOException{
    String status ="";
    try {
      URL url = new URL("URL here");
      FileOutputStream out = new FileOutputStream(new File("Vanilla.zip"));         // Output File
      out.write(url.openStream().readAllBytes());
      out.close();
    } catch (MalformedURLException e) {
      status = "Status: MalformedURLException Occured";
    }catch (IOException e) {
      status = "Status: IOexception Occured";
    }finally{
      status = "Status: Good";}
    String path="\\tkwgter5834\\";
    extractor(fname,"tkwgter5834",path);
    

    return status;
  }
  private String extractor(String fname,String dir,String path){
    File folder = new File(dir);
    if(!folder.exists()){
      folder.mkdir();
    }
    return "";
  }
}

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