2010-09-14 26 views
6

Anteriormente teníamos algunos archivos zip dentro de nuestra aplicación web. Quisiéramos emparejar un documento de texto específico dentro del archivo zip. Esto no fue un problema:Leyendo un archivo zip dentro de un archivo jar

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 
} 

Sin embargo, hemos movido estos archivos zip a otro módulo y queremos empaquetarlos dentro de un contenedor. Desafortunadamente, la creación del ZipFile ahora falla. Puedo obtener un InputStream para el zip: pero no tengo forma de obtener un flujo de entrada para la entrada en sí.

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(); 
} 

pero no tengo forma de obtener un flujo de entrada para la entrada en sí. Traté de encontrar la longitud de la entrada y obtener los siguientes n bytes desde el ZipInputStream pero esto no funcionó para mí. Parecía que todos los bytes leídos eran 0.

¿Hay alguna forma de evitar esto o voy a tener que mover los archivos zip de vuelta al proyecto central?

Respuesta

4

entrada puede darle el valor de entrada del archivo zip interno.

InputStream innerzipstream = zip.getInputStream(entry); 

Así que usted puede utilizar

new ZipInputStream(innerzipstream); 

y pedir al ZipInputStream para recuperar el contenido del-archivo zip interior (de una manera ordenada, que no tiene acceso aleatorio porque es un ZipInputStream)

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

acceso secuencial postal

Como ZipInputStream es la lectura de un archivo zip de una entrada de corriente que tiene que hacer las cosas en orden:

// 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(); 

Nota: No sé si ZipInputStream.getNextEntry() devuelve un valor nulo cuando el extremo de la cremallera archivo se alcanza o no. Eso espero porque no conozco otra forma de darse cuenta cuando ya no hay más entradas.

+0

De hecho, puede utilizar Class.getResourceAsStream y ZipInputStream para leer el exterior -zip también. De esta forma, no necesita reemplazar el nombre de archivo ad-hoc y no confíe en la URL para acceder a los archivos. – helios

6

¿Qué tal TrueZip? Utilizándolo, simplemente podría abrir el archivo comprimido como si estuviera ubicado dentro de un directorio.

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

De acuerdo con los documentos, también se admite el anidamiento infinito. Aún no he usado este proyecto, pero ha estado en mi radar por un tiempo y parece aplicable a su problema.

Sitio

Proyecto: http://truezip.java.net/ (editado)

0

he modificado el código de acceso Zip secuencial proporcionada anteriormente:

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(); 

En el código anterior, estoy intentando copiar un archivo JAR dentro de otro archivo Jar a una carpeta en el sistema de archivos. 'es' es el flujo de entrada al archivo jar dentro de otro archivo jar (jar.getInputStream ("lib/abcd.jar"))

0

También es posible analizar la cadena y abrir ZipInputStream en otro ZipInputStream y establecer la entrada al archivo dentro.

p. Ej. usted tiene la cadena como la de arriba "ruta/a/some-jar.jar/interno/zip/file.zip/miarchivo.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; 
} 
Cuestiones relacionadas