如何编写一个Java程序,可以提取JAR文件并将其数据存储在指定的目录(位置)中?

33

我创建了一个JAR文件。现在,我又创建了一个Java程序。我想要在其他目录中解压缩那个JAR文件,也就是说我想做一些像解压缩的事情。

如果我运行jar -xf filename.jar会导致一些错误:

Exception in thread "main" java.io.IOException: Cannot run program "jar": 
java.io.IOException: error=2, No such file or directory
     at java.lang.ProcessBuilder.start(ProcessBuilder.java:459)
     at java.lang.Runtime.exec(Runtime.java:593)`
10个回答

72

适应这个例子:如何从JAR和zip存档中提取Java资源

或者尝试这段代码:

Extract the Contents of ZIP/JAR Files Programmatically

Suppose jarFile is the jar/zip file to be extracted. destDir is the path where it will be extracted:

java.util.jar.JarFile jar = new java.util.jar.JarFile(jarFile);
java.util.Enumeration enumEntries = jar.entries();
while (enumEntries.hasMoreElements()) {
    java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement();
    java.io.File f = new java.io.File(destDir + java.io.File.separator + file.getName());
    if (file.isDirectory()) { // if its a directory, create it
        f.mkdir();
        continue;
    }
    java.io.InputStream is = jar.getInputStream(file); // get the input stream
    java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
    while (is.available() > 0) {  // write contents of 'is' to 'fos'
        fos.write(is.read());
    }
    fos.close();
    is.close();
}
jar.close();

Source: http://www.devx.com/tips/Tip/22124


3
这对其他人不起作用吗?我一直因为这个问题而收到FileNotFoundException的错误提示。 - Flafla2
5
它抛出异常的原因是,在创建“dest/folder1”之前,您实际上正在尝试编写类似“dest/folder1/file1”的内容。为解决此问题,建议您事先遍历JarEntry枚举,并将其转换为按getName().size()排序的列表(从小到大)。这样,您将在访问其子文件/子目录之前创建所有必要的目录。 - Destin
1
@frewper 看起来你可以使用 ZipFile 类来采用类似的方法处理 Zip 文件。 - jbranchaud
4
f.mkdir()移除,并将f.getParentFile().mkdirs()放置在if (file.isDirectory())块后,对我来说效果完美,无需排序等操作。 - user837703
2
为了解决异常,请在创建新文件后直接添加"f.getParentFile().mkdirs();"。 - simon
显示剩余8条评论

12

您可以使用此代码示例作为参考来完成任务。它与@JuanZe上面显示的代码片段几乎相同,但对于那些遇到FileNotFoundException的人,我添加了一个小代码片段,它将检查文件是否存在,如果不存在,则会创建父文件夹以及文件,并提取jar文件内容到指定的目标文件夹中。

代码片段:

public class JarDemo {

  public static void main(String[] args) throws java.io.IOException {
    java.util.jar.JarFile jarfile = new java.util.jar.JarFile(new java.io.File("E:/sqljdbc4.jar")); //jar file path(here sqljdbc4.jar)
    java.util.Enumeration<java.util.jar.JarEntry> enu= jarfile.entries();
    while(enu.hasMoreElements())
    {
        String destdir = "E:/abc/";     //abc is my destination directory
        java.util.jar.JarEntry je = enu.nextElement();

        System.out.println(je.getName());

        java.io.File fl = new java.io.File(destdir, je.getName());
        if(!fl.exists())
        {
            fl.getParentFile().mkdirs();
            fl = new java.io.File(destdir, je.getName());
        }
        if(je.isDirectory())
        {
            continue;
        }
        java.io.InputStream is = jarfile.getInputStream(je);
        java.io.FileOutputStream fo = new java.io.FileOutputStream(fl);
        while(is.available()>0)
        {
            fo.write(is.read());
        }
        fo.close();
        is.close();
    }

  }

}

这段代码片段适用于之前遇到文件未找到异常的人。 - stacky

5

旧问题。
这里是一个更新的答案,使用:

  • Java 7 java.nio 创建并高效地复制条目
  • Java 8 stream 按字典顺序排序和收集条目(以便始终先创建文件夹)。

请注意,我使用了java.util.zip.ZipFile(基类)而不是java.util.jar.JarFile(子类)。
后者执行更多不需要“仅”从存档中提取文件的事情。
因此,它减少了开销并防止与安全相关的异常。 但如果需要,您当然可以将ZipFile / ZipEntry替换为JarFile / JarEntry

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class FileUtils {

    public static void extractArchive(Path archiveFile, Path destPath) throws IOException {

        Files.createDirectories(destPath); // create dest path folder(s)

        try (ZipFile archive = new ZipFile(archiveFile.toFile())) {

            // sort entries by name to always create folders first
            List<? extends ZipEntry> entries = archive.stream()
                                                      .sorted(Comparator.comparing(ZipEntry::getName))
                                                      .collect(Collectors.toList());

            // copy each entry in the dest path
            for (ZipEntry entry : entries) {
                Path entryDest = destPath.resolve(entry.getName());

                if (entry.isDirectory()) {
                    Files.createDirectory(entryDest);
                    continue;
                }

                Files.copy(archive.getInputStream(entry), entryDest);
            }
        }

    }
}

5
JarFile类。 JarFile是一个类名。
JarFile file = new JarFile("file.jar");   
for (Enumeration<JarEntry> enum = file.entries(); enum.hasMoreElements();) {   
    JarEntry entry = enum.next();   
    System.out.println(entry.getName());   
} 

是的,我想要这样的东西,但是我该如何将提取的文件存储到其他目录中呢?你能指导我吗? - Sunil Kumar Sahoo
@gumuruh,我在2009年10月7日发布了答案,你能看到吗? - KV Prajapati

4

以下是我从jar包中提取整个“资源”文件夹的方法。使用BufferedReader和BufferedWriter会更快。

 public static boolean extractResourcesToTempFolder() {
    try {
        //If folder exist, delete it.
        String destPath = getTempDir() + File.separator + "JToolkit" + File.separator;
        deleteDirectoryRecursive(new File(destPath));            

        JarFile jarFile = new JarFile(JToolkit.class.getProtectionDomain().getCodeSource().getLocation().getPath());
        Enumeration<JarEntry> enums = jarFile.entries();
        while (enums.hasMoreElements()) {
            JarEntry entry = enums.nextElement();
            if (entry.getName().startsWith("resources")) {
                File toWrite = new File(destPath + entry.getName());
                if (entry.isDirectory()) {
                    toWrite.mkdirs();
                    continue;
                }
                InputStream in = new BufferedInputStream(jarFile.getInputStream(entry));
                OutputStream out = new BufferedOutputStream(new FileOutputStream(toWrite));
                byte[] buffer = new byte[2048];
                for (;;) {
                    int nBytes = in.read(buffer);
                    if (nBytes <= 0) {
                        break;
                    }
                    out.write(buffer, 0, nBytes);
                }
                out.flush();
                out.close();
                in.close();
            }
            System.out.println(entry.getName());
        }
    } catch (IOException ex) {
        Logger.getLogger(Methods.class.getName()).log(Level.SEVERE, null, ex);
        return false;
    }
    return true;
}

使用缓冲读取器,它可以在几秒钟内完成。巨大的区别。谢谢。 - Anoop Isaac

1

您的标题似乎与问题不太匹配,但如果您真的想要“编写一个提取jar文件的Java程序”,您只需要使用Class JarFile


是的,我想编写一个Java程序,可以提取一个JAR文件。 - Sunil Kumar Sahoo

1
你可以使用这个非常简单的库来打包/解压jar文件。

JarManager

非常简单。
import java.io.File;
import java.util.List;

import fr.stevecohen.jarmanager.JarUnpacker;

class Test {
   JarUnpacker jarUnpacker = new JarUnpacker(); 
   File myfile = new File("./myfile.jar");
   File unpackDir = new File("./mydir");

   List<File> unpacked_files = jarUnpacker.unpack(myfile.getAbsolutePath(), unpackDir.getAbsolutePath());
}

你还可以使用maven依赖
<dependency>
    <groupId>fr.stevecohen.jarmanager</groupId>
    <artifactId>JarManager</artifactId>
    <version>0.5.0</version>
</dependency>

你还需要我的代码库。

<repository>
    <id>repo-reapersoon</id>
    <name>ReaperSoon's repo</name>
    <url>http://repo-maven.stevecohen.fr</url>
</repository>

请使用下面的链接检查最新版本以使用最新的依赖项

如果您发现一些错误,请使用我的公共问题跟踪器


0

这是我的版本,使用 try-with-resources

    try (JarFile jarFile = new JarFile(artifact.getFile())) {
        for (JarEntry entry : Collections.list(jarFile.entries())) {
            try (InputStream is = jarFile.getInputStream(entry)) {
                File file = new File(targetDir, entry.getName());
                try (FileOutputStream fos = new FileOutputStream(file)) {
                    fos.write(is.read());
                }
            }
        }
    } catch (IOException e) {
        throw new MyException(String.format(
            "Unable to open jar %s", artifact.getArtifactId()), e);
    }

0
这是我的版本,使用 Java 8org.apache.commons.io 库,对我很有效。
File userDir = new File("resources");
userDir.mkdir();
JarFile jar = new JarFile("123.jar");
jar.stream()
   .filter(file -> 
    file.getName().contains("BOOT-INF/classes") && !file.getName().contains("BOOT-INF/classes/ch/"))
   .peek(file -> {
File res = new File(userDir.getPath() + file.getName().replaceAll("BOOT-INF/classes", ""));

     if (file.isDirectory()) {
          res.mkdirs();
     } else { try {
                  FileUtils.copyInputStreamToFile(jar.getInputStream(file), res);
            } catch (IOException e) {
                  System.out.println(e.getMessage());
            }
     }
     }).forEach(file -> LOGGER.info("Extract file: " + file.getName()));

0
如果有人感兴趣的话...
这是一个适用于Java 7+的版本,使用缓冲I/O和try-with-resources。
public static void unzip(File archive, File destDir) throws IOException {
    byte[] buffer = new byte[256 * 1024];
    destDir.mkdirs();
    try (JarFile jar = new JarFile(archive)) {
        Enumeration<JarEntry> entries = jar.entries();
        while (entries.hasMoreElements()) {
            JarEntry ent = entries.nextElement();
            File f = new File(destDir, ent.getName());
            if (ent.isDirectory()) {
                f.mkdir();
                continue;
            }
            try (InputStream is = jar.getInputStream(ent);
                 FileOutputStream os = new FileOutputStream(f)) {
                for (int r; (r = is.read(buffer)) > 0; ) {
                    os.write(buffer, 0, r);
                }
            }
        }
    }
}

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