2012-04-03 5 views

risposta

16

Non c'è attualmente un modo per fare questo genere di cose dalla libreria standard Scala, ma è abbastanza facile da usare 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() 
} 

mi sto concentrando sulla semplicità invece di efficienza qui (verifica nessun errore leggere e scrivere un byte alla volta non è l'ideale), ma funziona e può essere facilmente migliorato.

+0

Si dovrebbe 'in.close()' quando hai finito con 'tale dicitura. – leedm777

+0

Sì, certo, l'ho risolto ora. –

+0

E so che non è ottimizzato, ma non è necessario avvolgere il byte in un array. Puoi semplicemente 'zip.write (b)'. – leedm777

3

Questo è un po 'più stile scala nel caso in cui vi piace funzionale:

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

e non dimenticate le importazioni:

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

'readByte (in) .takeWhile (_> -1) .toList' consuma molta memoria durante la lettura di file di grandi dimensioni. L'uso di 'Iterator' potrebbe essere migliore. – jilen

+0

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

3

La risposta Travis è corretto ma io abbiamo ottimizzato un poco per ottenere una versione più veloce del suo codice:

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

Recentemente ho dovuto lavorare con i file zip troppo e ho trovato questo molto bello utility: https://github.com/zeroturnaround/zt-zip

Ecco un esempio di zippare tutti i file all'interno di una directory:

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

Molto comodo.

1

Un po 'di versione (più breve) utilizzando NIO2 modificato:

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() 
} 
Problemi correlati