我想将先前从其他文件中提取的一系列文件(已完成)添加到一个jar包中。这些文件将覆盖JAR包内部的文件。最有效的方法是什么? 我需要速度快。 谢谢!
jar -uf my.jar file1 file2...
jar -uf my.jar dir/
或混合
jar -uf my.jar file dir/
jar -u file.jar file1 file2 file3 ...
JAR文件是ZIP文件,记住这一点。
只需使用一些ZIP库即可。
补充现有回答,还存在至少一种特殊情况:所谓的可执行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
0
选项:jar uvf0 myfile.jar BOOT-INF/lib/file.jar
对于普通的类文件,您不需要这个。
//Add a file in jar in a particular folder
jar uvf <jar file name> <file to be added> <folder name inside jar>
<jar文件内的文件夹名称>
中使用什么?我的用例是src/config.properties
。我尝试过使用jar --update --file=../demo.jar --main-class=demo.App src/config.properties <???>
。 - Adobe zip file.jar file1 file2 file3
在我的Mac Os 10.7.5上运行良好。
-C
jar选项非常有用。$ jar uf jar-file -C /path/to/my_jars/ this_useful.jar
$ jar tf jar-file | grep this_useful.jar
this_useful.jar
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);
}
}
}
}
这里是将目录内容复制到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];
}
}
这里您可以看到添加文件到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
jar -u
时,我不得不按Ctrl + C
才能让脚本继续运行(原因不明)。加上参数-f
就解决了这个问题。 - Stelios Adamantidis