如何将文件添加到Jar文件?

27

我想将先前从其他文件中提取的一系列文件(已完成)添加到一个jar包中。这些文件将覆盖JAR包内部的文件。最有效的方法是什么? 我需要速度快。 谢谢!

11个回答

28
jar -uf my.jar file1 file2...
jar -uf my.jar dir/

或混合

jar -uf my.jar file dir/

3
我不知道为什么这个答案得票率这么低。当我尝试在bash脚本中使用jar -u时,我不得不按Ctrl + C才能让脚本继续运行(原因不明)。加上参数-f就解决了这个问题。 - Stelios Adamantidis

21
jar -u file.jar file1 file2 file3 ...

2
JAR不过就是ZIP格式,所以 -zip -ur 文件.zip 目录名/带有文件的目录/ - jdevelop
1
它适用于Linux / Mac OS / Windows,但您需要为您的操作系统安装它。 - jdevelop
3
为什么您希望客户将某些内容添加到JAR文件中?如果需要,让他们指定包含文件的文件夹并读取它们即可。 - jdevelop
2
@zyngawow,如果使用者无法处理命令行,则应该能够使用他们喜欢的任何zip应用程序。我猜想,如果非技术用户需要以任何方式(CLI或GUI)调整存储在jar / zip文件中的设置,他们肯定会抱怨。 - Bruno
1
@zyngawow 可以像上面解释的方法那样做。其他人为什么要重复呢? - Baz
显示剩余4条评论

10

JAR文件是ZIP文件,记住这一点。

只需使用一些ZIP库即可。


2
如果你只是想偶尔扔一些文件,我在Windows 8.1上的做法是将文件的扩展名从.jar改为.zip。然后我可以使用Windows资源管理器浏览文件并根据需要添加/删除文件。完成后将其改回.jar并享受啤酒。 - IcedDante

7

补充现有回答,还存在至少一种特殊情况:所谓的可执行JAR文件。如果您将另一个JAR文件作为依赖项添加进来——无论是使用jar还是zip——它都会抱怨嵌入的文件被压缩:

Caused by: java.lang.IllegalStateException: Unable to open nested entry 'BOOT-INF/lib/file.jar'. It has been compressed and nested jar files must be stored without compression. Please check the mechanism used to create your executable jar file

解决方法是使用jar的0选项:
jar uvf0 myfile.jar BOOT-INF/lib/file.jar

对于普通的类文件,您不需要这个。


4
//Add a file in jar in a particular folder
jar uvf <jar file name> <file to be added> <folder name inside jar>

如何将文件添加到JAR文件的根目录——需要在<jar文件内的文件夹名称>中使用什么?我的用例是src/config.properties。我尝试过使用jar --update --file=../demo.jar --main-class=demo.App src/config.properties <???> - Adobe

3
 zip file.jar file1 file2 file3 

在我的Mac Os 10.7.5上运行良好。


1
扩展现有答案,我发现在添加单独文件夹中的文件并使其路径变平时,-C jar选项非常有用。
$ jar uf jar-file -C /path/to/my_jars/ this_useful.jar

你最终会在JAR的根目录下拥有这个useful.jar文件:
$ jar tf jar-file | grep this_useful.jar
this_useful.jar

1
如果有人需要编程答案,这里提供一种程序化的方法。
private static void createJar(File source, JarOutputStream target) {
    createJar(source, source, target);
}

private static void createJar(File source, File baseDir, JarOutputStream target) {
    BufferedInputStream in = null;

    try {
        if (!source.exists()) {
            throw new IOException("Source directory is empty");
        }
        if (source.isDirectory()) {
            // For Jar entries, all path separates should be '/'(OS independent)
            String name = source.getPath().replace("\\", "/");
            if (!name.isEmpty()) {
                if (!name.endsWith("/")) {
                    name += "/";
                }
                JarEntry entry = new JarEntry(name);
                entry.setTime(source.lastModified());
                target.putNextEntry(entry);
                target.closeEntry();
            }
            for (File nestedFile: source.listFiles()) {
                createJar(nestedFile, baseDir, target);
            }
            return;
        }

        String entryName = baseDir.toPath().relativize(source.toPath()).toFile().getPath().replace("\\", "/");
        JarEntry entry = new JarEntry(entryName);
        entry.setTime(source.lastModified());
        target.putNextEntry(entry); in = new BufferedInputStream(new FileInputStream(source));

        byte[] buffer = new byte[1024];
        while (true) {
            int count = in .read(buffer);
            if (count == -1)
                break;
            target.write(buffer, 0, count);
        }
        target.closeEntry();
    } catch (Exception ignored) {

    } finally {
        if ( in != null) {
            try { in .close();
            } catch (Exception ignored) {
                throw new RuntimeException(ignored);
            }
        }
    }
}

0

这里是将目录内容复制到JAR文件的另一个示例。

/**
 * Copy source directory to a folder inside JAR file.
 * @param directory
 * @param jarFile
 * @param jarFolder
 * @throws Exception
 */
protected void copyDirectoryToJar(String directory, String jarFile, String jarFolder)
        throws Exception {
    
    // Initialize local variables.
    FileSystem destinationJarFileSystem = null;
    Exception exception = null;
    try {
        // Get source path.
        final Path sourcePath = Paths.get(directory);
        
        // Get destination JAR file system and destination path inside the JAR file.
        final URI uri = URI.create("jar:file:/" + jarFile.replace(File.separatorChar, '/'));
        final Map<String, String> environment = Map.of("create", "true");
        destinationJarFileSystem = FileSystems.newFileSystem(uri, environment);
        final Path destinationPath = destinationJarFileSystem.getPath(jarFolder);

        // Copy source directory into target JAR file.
        copyFromDirToJar(sourcePath, destinationPath, destinationJarFileSystem);
    }
    catch (Exception e) {
        exception = e;
    }
    finally {
        // Close JAR file systems.
        try {
            if (destinationJarFileSystem != null) {
                destinationJarFileSystem.close();
            }
        }
        catch (Exception e) {
            if (exception == null) {
                exception = e;
            }
        }
    }
    
    // Throw exception.
    if (exception != null) {
        throw exception;
    }   
}

/* Recursively copy the source sub directories and files to target JAR file system.
 * @param sourcePath
 * @param destinationPath
 * @param destinationFileSystem
 */
private static void copyFromDirToJar(Path sourcePath, Path destinationPath, FileSystem destinationFileSystem)
        throws Exception {
    
    // Create destination directory if it doesn't exist.
    if (!Files.exists(destinationPath)) {
        Files.createDirectories(destinationPath);
    }
    
    // If the source and destination paths designate files, copy the source
    // file directly to the destination file.
    if (Files.isRegularFile(sourcePath) && Files.isRegularFile(destinationPath)) {
        Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
    }
    
    // List sub directories in the source path.
    Exception [] exception = new Exception [] {null};
    Files.list(sourcePath).forEachOrdered(sourceSubPath -> {
        try {
            Path fileOrFolder = sourceSubPath.getFileName();
            Path destinationSubPath = destinationFileSystem.getPath(destinationPath.toString(), fileOrFolder.toString());
            
            // Copy sub directories recursively or copy a single file.
            if (Files.isDirectory(sourceSubPath)) {
                copyFromDirToJar(sourceSubPath, destinationSubPath, destinationFileSystem);
            }
            else {
                Files.copy(sourceSubPath, destinationSubPath, StandardCopyOption.REPLACE_EXISTING);
            }
        }
        catch (Exception e) {
            exception[0] = e;
        }
    });
    
    // Throw exception.
    if (exception[0] != null) {
        throw exception[0];
    }
}

0
如果您想知道如何将位于/host/path/file.txt的文件添加到/jar/path/newfilename.txt,那么不幸的是答案是不能。您只能在您想要在jar文件中的主机上创建目录结构和文件名。正如其他回答所提到的,jar只是一个zip归档文件,因此您可以使用zip命令来添加文件,但是相同的限制似乎也适用。请查看有关zips的相关问题:linux how to add a file to a specific folder within a zip filehttps://unix.stackexchange.com/questions/621321/add-file-in-a-sub-directory-of-a-zip

这里您可以看到添加文件到JAR时的一些行为

~ ls
HelloWorld.class HelloWorld.java
~ jar cf HelloWorld.jar HelloWorld.class
~ jar tf HelloWorld.jar
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
~ mkdir -p path/on/host
~ touch path/on/host/newfile.txt
~ jar uf HelloWorld.jar path/on/host/
~ jar tf HelloWorld.jar
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
path/on/host/
path/on/host/newfile.txt
~ mkdir -p otherpath/on/host
~ touch otherpath/on/host/otherfile.txt
~ jar uf HelloWorld.jar -C otherpath/on/host/ ./
~ jar tf HelloWorld.jar
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
path/on/host/
path/on/host/newfile.txt
otherfile.txt

-C标志似乎与在运行jar uf之前CD到目录的操作完全相同。它不能将文件复制到JAR包中不同于主机上的路径。

这里有一个小脚本,在tmp目录中创建JAR的目标目录结构,然后将其复制过去:

# copyToJarAtPath.sh
# usage: ./copyToJarAtPath.sh <path to jar> <source path> <target path in jar>
set -x
set -e
echo "jar: '$1', host path: '$2', target path: '$3'"
tmpd=$(mktemp -d)
echo "temp dir: $tmpd"
mkdir -p "$tmpd/$(dirname "$3")"
cp -R $2 "$tmpd/$3"
jar uf $1 -C $tmpd $3

这适用于单个文件或目录。不过对于带有空格的路径,可能需要进行一些调整。 使用示例(脚本回显输出已修剪):

# single file

~ ./copyToJarAtPath.sh HelloWorld.jar path/on/host/newfile.txt a/b/c.txt
~ jar tf HelloWorld.jar
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
path/on/host/
path/on/host/newfile.txt
a/b/c.txt

# directory 

~ mkdir -p foo/bar/baz
~ touch foo/bar/baz/image.png
~ ./copyToJarAtPath.sh HelloWorld.jar foo/bar/baz new/img/path
~ jar tf HelloWorld.jar
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
path/on/host/
path/on/host/newfile.txt
a/b/c.txt
new/img/path/
new/img/path/image.png

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