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

Posted

技术标签:

【中文标题】从 Zip 文件中的文件中读取内容【英文标题】:Read Content from Files which are inside Zip file 【发布时间】:2013-03-18 00:32:25 【问题描述】:

我正在尝试创建一个简单的 java 程序,该程序从 zip 文件中的文件中读取和提取内容。 Zip 文件包含 3 个文件(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();
        
    

【问题讨论】:

为什么不将 zip 文件直接传递给 Apache Tika?然后它会调用你为 zip 中的每个文件提供的递归解析器,所以你不必做任何特别的事情! 这就是我想知道的,但无法获得足够的教程来说明如何做到这一点。我也有点担心 - javamex.com/tutorials/compression/zip_problems.shtml,不确定 Tika 是否解决了这个问题。 Tika 使用 commons compress 解决很多这些问题 61 Mb 用于 Tika? 61 Mb 仅用于使用 ZIP,可以使用约 10 个字符串完成?!我的应用程序有 15 个以上的活动,重量小于 4 Mb。我认为只有为琐碎的任务而拥有这么大的应用程序是对用户的不尊重。 【参考方案1】:

由于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 希望您已经尝试过,并意识到在 while 块内不会有对 ZipEntry 的引用。如果您愿意,这也可以:ZipEntry entry = zip.getNextEntry(); while (entry !=null) /* do stuff */ entry = zip.getNextEntry(); 【参考方案2】:

如果您想知道如何从每个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,你就可以随心所欲地阅读它。

【讨论】:

不要忘记关闭 inputStream 和 ZipFile 以避免资源泄漏:)。 zipFile.entries();没有为类型 zipFile 定义条目函数 有没有办法将 byte[] 数组传递给ZipFile (content.getBytes()) 的构造函数?如果不是,我们该怎么做? @Simple-Solution 我认为最简单的方法是将字节数组写入新的File,并将该File 实例提供给构造函数 终极解决方案 +1【参考方案3】:

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

据我所知,接受的解决方案不适用于嵌套 zip 文件的情况。不过,Tika 也会处理这种情况。

【讨论】:

【参考方案4】:

我实现这一点的方法是创建 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();
    

它的用途:

    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();

这种方法的一个优点:InputStreams 作为参数传递给处理它们的方法,并且这些方法倾向于在处理完输入流后立即关闭它。

【讨论】:

【参考方案5】:

从 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);
            
        );
    

【讨论】:

这太棒了,也太疯狂了。 FileSystem应该在操作后关闭。 在 java 8 版本中,Files.walk(root) 抛出无法通过 lambda 传播的 IOException。【参考方案6】:

我就是这样做的,记得更改 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 "";
  

【讨论】:

以上是关于从 Zip 文件中的文件中读取内容的主要内容,如果未能解决你的问题,请参考以下文章

从 Python3 中的 .zip 文件中提取和读取 [重复]

使用 Haskell 的 zip-conduit 从 zip 存档中的文件中读取行

Python:如何从内存中的 zip 文件中读取图像?

java 读取zip里面的文件夹

如何使用 python 从位于同一目录中的多个 zip 文件夹中读取 csv 文件?

是否可以直接从存储在 S3 上的 zip 文件中读取特定文件?