2012-04-03 13 views

Respuesta

16

No hay actualmente ninguna manera de hacer este tipo de cosas de la librería estándar Scala, pero es bastante fácil de usar java.util.zip:

def zip(out: String, files: Iterable[String]) = { 
    import java.io.{ BufferedInputStream, FileInputStream, FileOutputStream } 
    import java.util.zip.{ ZipEntry, ZipOutputStream } 

    val zip = new ZipOutputStream(new FileOutputStream(out)) 

    files.foreach { name => 
    zip.putNextEntry(new ZipEntry(name)) 
    val in = new BufferedInputStream(new FileInputStream(name)) 
    var b = in.read() 
    while (b > -1) { 
     zip.write(b) 
     b = in.read() 
    } 
    in.close() 
    zip.closeEntry() 
    } 
    zip.close() 
} 

estoy centrado en la simplicidad en lugar de la eficiencia aquí (comprobación de ningún error y leer y escribir un byte a la vez no es ideal), pero funciona y puede mejorarse muy fácilmente.

+0

Usted debe 'in.close()' cuando haya terminado con 'in'. – leedm777

+0

Sí, por supuesto, lo he arreglado ahora. –

+0

Y sé que no está optimizado, pero no hay necesidad de envolver el byte en una matriz. Simplemente puede 'zip.write (b)'. – leedm777

3

Esto es un poco más estilo Scala en caso de que como funcional:

def compress(zipFilepath: String, files: List[File]) { 
    def readByte(bufferedReader: BufferedReader): Stream[Int] = { 
     bufferedReader.read() #:: readByte(bufferedReader) 
    } 
    val zip = new ZipOutputStream(new FileOutputStream(zipFilepath)) 
    try { 
     for (file <- files) { 
     //add zip entry to output stream 
     zip.putNextEntry(new ZipEntry(file.getName)) 

     val in = Source.fromFile(file.getCanonicalPath).bufferedReader() 
     try { 
      readByte(in).takeWhile(_ > -1).toList.foreach(zip.write(_)) 
     } 
     finally { 
      in.close() 
     } 

     zip.closeEntry() 
     } 
    } 
    finally { 
     zip.close() 
    } 
    } 

y no se olvide de las importaciones:

import java.io.{BufferedReader, FileOutputStream, File} 
import java.util.zip.{ZipEntry, ZipOutputStream} 
import io.Source 
+0

'readByte (in) .takeWhile (_> -1) .toList' consumirá mucha memoria mientras lee un archivo grande. Usar 'Iterator' puede ser mejor. – jilen

+0

'def readByte (bufferedReader: BufferedReader) = Stream.continually (bufferedReader.read())' – nafg

3

La respuesta Travis es correcta pero hemos modificado una poco para obtener una versión más rápida de su código:

val Buffer = 2 * 1024 

def zip(out: String, files: Iterable[String], retainPathInfo: Boolean = true) = { 
    var data = new Array[Byte](Buffer) 
    val zip = new ZipOutputStream(new FileOutputStream(out)) 
    files.foreach { name => 
    if (!retainPathInfo) 
     zip.putNextEntry(new ZipEntry(name.splitAt(name.lastIndexOf(File.separatorChar) + 1)._2)) 
    else 
     zip.putNextEntry(new ZipEntry(name)) 
    val in = new BufferedInputStream(new FileInputStream(name), Buffer) 
    var b = in.read(data, 0, Buffer) 
    while (b != -1) { 
     zip.write(data, 0, b) 
     b = in.read(data, 0, Buffer) 
    } 
    in.close() 
    zip.closeEntry() 
    } 
    zip.close() 
} 
5

Recientemente tuve que trabajar con archivos zip también y pareció muy simpática utilidad: https://github.com/zeroturnaround/zt-zip

Aquí está un ejemplo de comprimir todos los archivos dentro de un directorio:

import org.zeroturnaround.zip.ZipUtil 
ZipUtil.pack(new File("/tmp/demo"), new File("/tmp/demo.zip")) 

muy conveniente.

1

Un poco versión modificada (más corto) usando NIO2:

private def zip(out: Path, files: Iterable[Path]) = { 
    val zip = new ZipOutputStream(Files.newOutputStream(out)) 

    files.foreach { file => 
    zip.putNextEntry(new ZipEntry(file.toString)) 
    Files.copy(file, zip) 
    zip.closeEntry() 
    } 
    zip.close() 
} 
Cuestiones relacionadas