java 读取 jar 文件中的 zip 文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3710615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 03:06:32  来源:igfitidea点击:

Reading a zip file within a jar file

javajarzip

提问by samblake

Previously we had some zip files within our web application. We would want to pares a specific text document within the zip file. This wasn't a problem:

以前,我们的 Web 应用程序中有一些 zip 文件。我们想要在 zip 文件中删除特定的文本文档。这不是问题:

URL url = getClass().getResource(zipfile);
ZipFile zip = new ZipFile(url.getFile().replaceAll("%20", " "));     
Entry entry = zip.getEntry("file.txt");

InputStream is = zip.getInputStream(entry);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));

String line = reader.readLine();
while (line != null) {
    // do stuff
}

However we've moved these zip files into another module and want to package them within a jar. Unfortunately, creating the ZipFilenow fails. I can get an InputStreamfor the zip: but I have no way of getting an input stream for the entry itself.

然而,我们已经将这些 zip 文件移动到另一个模块中,并希望将它们打包在一个 jar 中。不幸的是,创建ZipFile现在失败了。我可以得到一个InputStreamzip: 但我无法获得条目本身的输入流。

InputStream is = getClass().getResourceAsStream(zipfile);
ZipInputStream zis = new ZipInputStream(is);

ZipEntry entry = zis.getNextEntry();
while (entry != null && !entry.getName().equals("file.txt")) {
    entry = zis.getNextEntry();
}

but I have no way of getting an input stream for the entry itself. I tried finding the length of the entry and getting the next n bytes from the ZipInputStreambut this didn't work for me. It seemed all bytes read were 0.

但我无法获得条目本身的输入流。我尝试找到条目的长度并从中获取接下来的 n 个字节,ZipInputStream但这对我不起作用。似乎所有读取的字节都是 0。

Is there a way round this or am I going to have to move the zip files back into the core project?

有没有办法解决这个问题,还是我必须将 zip 文件移回核心项目?

采纳答案by helios

entry can give you the inputstream of the inner-zip file.

entry 可以为您提供内部 zip 文件的输入流。

InputStream innerzipstream = zip.getInputStream(entry);

So you can use

所以你可以使用

new ZipInputStream(innerzipstream);

and ask the ZipInputStream to retrieve the content of the inner-zip-file (in an ordered fashion, you don't have random access because it's a ZipInputStream)

并要求 ZipInputStream 检索内部 zip 文件的内容(以有序的方式,您没有随机访问,因为它是一个 ZipInputStream)

Look at http://download.oracle.com/javase/1.4.2/docs/api/java/util/zip/ZipInputStream.html

查看http://download.oracle.com/javase/1.4.2/docs/api/java/util/zip/ZipInputStream.html

Sequential zip access

顺序 zip 访问

As ZipInputStream is reading a zip file from an input stream it has to do things in order:

当 ZipInputStream 从输入流中读取 zip 文件时,它必须按顺序执行以下操作:

// DO THIS for each entry
ZipEntry e = zipInputStreamObj.getNextEntry();
e.getName // and all data
int size = e.getSize(); // the byte count
while (size > 0) {
   size -= zipInputStreamObj.read(...);
}
zipInputStreamObj.closeEntry();
// DO THIS END

zipInputStreamObj.close();

Note:I don't know if ZipInputStream.getNextEntry() returns null when end of zip file is reached or not. I hope so because I don't know other way to realize when there are no more entries.

注意:当到达 zip 文件末尾时,我不知道 ZipInputStream.getNextEntry() 是否返回 null。我希望如此,因为当没有更多条目时,我不知道其他方式来实现。

回答by Andy

How about TrueZip? Using it you could simply open the zipped file as if it was located inside a directory.

TrueZip 怎么样?使用它,您可以简单地打开压缩文件,就像它位于目录中一样。

new FileOutputStream("/path/to/some-jar.jar/internal/zip/file.zip/myfile.txt");

According to the docs, infinite nesting is also supported. I have yet to actually use this project, but it's been on my radar for a while and it seems applicable to your problem.

根据文档,还支持无限嵌套。我还没有真正使用过这个项目,但它已经在我的关注范围内有一段时间了,它似乎适用于您的问题。

Project Site: http://truezip.java.net/(edited)

项目站点:http: //truezip.java.net/(已编辑)

回答by Tilak Sharma

I have modified the Sequential Zip access code provided above:

我修改了上面提供的顺序邮政编码:

File destFile = new File(destDir, jarName);
JarOutputStream jos = new JarOutputStream(new FileOutputStream(destFile));

JarInputStream jis = new JarInputStream(is);
JarEntry jarEntry = jis.getNextJarEntry();
for (; jarEntry != null ; jarEntry = jis.getNextJarEntry()) {
    jos.putNextEntry(new JarEntry(jarEntry.getName()));
    if(jarEntry.isDirectory()) {
       continue;
    }

    int bytesRead = jis.read(buffer);
    while(bytesRead != -1) {
    jos.write(buffer, 0, bytesRead);
    bytesRead = jis.read(buffer);
    }

}
is.close();
jis.close();
jos.flush();
jos.closeEntry();
jos.close();

In the above code, I am trying to copy a Jar file inside another Jar file to a folder in file system. 'is' is the input stream to the jar file inside another jar file (jar.getInputStream("lib/abcd.jar"))

在上面的代码中,我试图将另一个 Jar 文件中的 Jar 文件复制到文件系统中的文件夹中。'is' 是另一个 jar 文件中 jar 文件的输入流 (jar.getInputStream("lib/abcd.jar"))

回答by Ben

it is also possible to parse the string and open an ZipInputStream on another ZipInputStream and set the entry to the file inside.

也可以解析字符串并在另一个 ZipInputStream 上打开一个 ZipInputStream 并将条目设置为里面的文件。

e.g. you have the String like above "path/to/some-jar.jar/internal/zip/file.zip/myfile.txt"

例如你有像上面这样的字符串“path/to/some-jar.jar/internal/zip/file.zip/myfile.txt”

private static final String[] zipFiles = new String[] { ".zip", ".jar" };

public static InputStream getResourceAsStream(final String ref) throws IOException {
    String abstractPath = ref.replace("\", "/");
    if (abstractPath.startsWith("/")) {
        abstractPath = abstractPath.substring(1);
    }
    final String[] pathElements = abstractPath.split("/");
    return getResourceAsStream(null, pathElements);
}

private static InputStream getResourceAsStream(final ZipInputStream parentStream, final String[] pathElements)
        throws IOException {

    if (pathElements.length == 0) return parentStream;

    final StringBuilder nextFile = new StringBuilder();
    for (int index = 0; index < pathElements.length; index++) {
        final String pathElement = pathElements[index];
        nextFile.append((index > 0 ? "/" : "") + pathElement);
        if (pathElement.contains(".")) {
            final String path = nextFile.toString();
            if (checkForZip(pathElement)) {
                final String[] restPath = new String[pathElements.length - index - 1];
                System.arraycopy(pathElements, index + 1, restPath, 0, restPath.length);
                if (parentStream != null) {
                    setZipToEntry(parentStream, path);
                    return getResourceAsStream(new ZipInputStream(parentStream), restPath);
                } else return getResourceAsStream(new ZipInputStream(new FileInputStream(path)), restPath);
            } else {
                if (parentStream != null) {
                    setZipToEntry(parentStream, path);
                    return parentStream;
                } else return new FileInputStream(path);
            }
        }
    }
    throw new FileNotFoundException("File not found: " + nextFile.toString());
}

private static void setZipToEntry(final ZipInputStream in, final String name) throws IOException {
    ZipEntry entry;
    while ((entry = in.getNextEntry()) != null) {
        if (entry.getName().equals(name)) return;
    }
    throw new FileNotFoundException("File not found: " + name);
}

private static boolean checkForZip(final String ref) {
    for (final String zipFile : zipFiles) {
        if (ref.endsWith(zipFile)) return true;
    }
    return false;
}