Java 如何使用JarOutputStream创建JAR文件?
如何使用Java 如何使用JarOutputStream创建JAR文件?,java,jar,Java,Jar,如何使用java.util.JAR.JarOutputStream以编程方式创建JAR文件?我的程序生成的JAR文件看起来是正确的(它提取得很好),但当我尝试从中加载库时,Java抱怨它找不到明确存储在其中的文件。如果我提取JAR文件并使用Sun的JAR命令行工具重新压缩它,那么生成的库就可以正常工作。简而言之,我的JAR文件有问题 请解释如何以编程方式创建JAR文件,包括清单文件。以下是一些使用JarOutputStream创建JAR文件的示例代码: 事实证明,JarOutputStrea
java.util.JAR.JarOutputStream
以编程方式创建JAR文件?我的程序生成的JAR文件看起来是正确的(它提取得很好),但当我尝试从中加载库时,Java抱怨它找不到明确存储在其中的文件。如果我提取JAR文件并使用Sun的JAR
命令行工具重新压缩它,那么生成的库就可以正常工作。简而言之,我的JAR文件有问题
请解释如何以编程方式创建JAR文件,包括清单文件。以下是一些使用JarOutputStream创建JAR文件的示例代码:
JarOutputStream
有三个未记录的怪癖:
public void run() throws IOException
{
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream("output.jar"), manifest);
add(new File("inputDirectory"), target);
target.close();
}
private void add(File source, JarOutputStream target) throws IOException
{
BufferedInputStream in = null;
try
{
if (source.isDirectory())
{
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())
add(nestedFile, target);
return;
}
JarEntry entry = new JarEntry(source.getPath().replace("\\", "/"));
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();
}
finally
{
if (in != null)
in.close();
}
}
还有另一个需要注意的“怪癖”:所有JarEntry的名字都不应该以“/”开头
例如:清单文件的jar条目名是“META-INF/manifest.MF”,而不是“/META-INF/manifest.MF”
所有jar条目都应遵循相同的规则。您可以使用以下代码:
public void write(File[] files, String comment) throws IOException {
FileOutputStream fos = new FileOutputStream(PATH + FILE);
JarOutputStream jos = new JarOutputStream(fos, manifest);
BufferedOutputStream bos = new BufferedOutputStream(jos);
jos.setComment(comment);
for (File f : files) {
print("Writing file: " + f.toString());
BufferedReader br = new BufferedReader(new FileReader(f));
jos.putNextEntry(new JarEntry(f.getName()));
int c;
while ((c = br.read()) != -1) {
bos.write(c);
}
br.close();
bos.flush();
}
bos.close();
// JarOutputStream jor = new JarOutputStream(new FileOutputStream(PATH + FILE), manifest);
}
PATH
变量: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);
}
}
}
}
根据请求,这里是Gili的代码,修改为使用相对路径而不是绝对路径。(将“inputDirectory”替换为您选择的目录。)我刚刚测试了它,但如果它不起作用,请让我知道
public void run() throws IOException
{
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream("output.jar"), manifest);
File inputDirectory = new File("inputDirectory");
for (File nestedFile : inputDirectory.listFiles())
add("", nestedFile, target);
target.close();
}
private void add(String parents, File source, JarOutputStream target) throws IOException
{
BufferedInputStream in = null;
try
{
String name = (parents + source.getName()).replace("\\", "/");
if (source.isDirectory())
{
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())
add(name, nestedFile, target);
return;
}
JarEntry entry = new JarEntry(name);
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();
}
finally
{
if (in != null)
in.close();
}
}
我已经在做了。事实上,您引用的示例没有指出必须对目录名显式地putnextry()或调用JarOutputStream.closeEntry()。一定是出了什么事。啊,好的。在没有看到任何代码的情况下,要提供更好的解决方案有点困难,所以我只是向您指出了那个参考。很高兴你找到了答案。谢谢你的帮助。非常感谢。也许您应该展示您当前的(非工作)解决方案这些“怪癖”实际上是zip规范的一部分(jar文件只是带有清单和不同扩展名的zip文件)。我同意应该将其记录在API文档中,不过-我建议打开一个问题(),更重要的是,API应该防止您创建无效的ZIP/JAR文件,如果您传入了错误类型的斜杠,则会抛出异常,或者自动转换它们。对于以斜杠结尾的目录,应该明确记录它,因为没有办法自动更正它。我提交了一份bug报告,但还没有被接受。仅供参考-Zip spec:-搜索“文件名:(变量)”@Gili api无法“阻止”您使用“错误”的斜杠;因为,“错误”斜杠是文件名中的有效字符。并非所有操作系统都将“\”识别为目录分隔符,以及那些不允许(不包括目录)文件名为“he\he\he”的操作系统。此程序生成奇怪的输出$ls MyClass.class output.jar使用此程序我创建了jar output.jar$jar tf output.jar META-INF/MANIFEST.MF/Users/aniath/Documents/workspace/interview/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work/Catalina/localhost/mywebapp2/tmp//Users/aniath/Documents/workspace/interview/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work/Catalina/localhost/mywebapp2/tmp/MyClass.class/Users/aniath/Documents/workspace/interview/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work/Catalina/localhost/mywebapp2/tmp/output.jar这又增加了什么,接受的答案没有已经说了吗?被接受的答案比我的答案使用了更多的代码。我认为,当你能写一点的时候,你不想写太多的代码。公平地说,你的答案包含更少的代码,因为它做的更少。接受的答案包含将输入转换为JarOutputStream所期望的格式的代码。如果您在Windows下运行,或者如果目录名不以斜杠结尾,代码将以静默方式失败。但是,使用接受的答案获取ClassFormatError是可行的。如果在这两者之间引发异常,则流不会关闭。您必须使用try finally或try with resources来保证流将被关闭。这应该作为对已接受答案的评论发布,因为它没有回答主要问题。