2012-04-03 15 views

Odpowiedz

16

Nie ma obecnie jakikolwiek sposób zrobić tego rodzaju rzeczy z biblioteki standardowej Scala, ale jest to dość łatwy w użyciu 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() 
} 

jestem koncentrując się na prostotę zamiast efektywności tutaj (sprawdzanie bez błędów a czytanie i pisanie jednego bajtu na raz nie jest idealne), ale działa i można je bardzo łatwo poprawić.

+0

Powinieneś 'in.close()' kiedy skończysz z 'in'. – leedm777

+0

Tak, oczywiście - naprawiłem to teraz. –

+0

Wiem, że nie jest zoptymalizowany, ale nie ma potrzeby pakowania bajtu w tablicę. Możesz po prostu 'zip.write (b)'. – leedm777

3

To jest trochę bardziej styl scala w przypadku lubisz funkcjonalny:

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

i nie zapomnij importu:

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

'readByte (in) .takeWhile (_> -1) .toList' zużyje dużo pamięci podczas czytania dużego pliku. Używanie 'Iterator' może być lepsze. – jilen

+0

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

3

Travis odpowiedź jest poprawna, ale mam manipulowane niewiele, aby uzyskać szybszą wersję swojego kodu:

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

Niedawno musiałem pracować z plikami zip zbyt i okazało się to bardzo miłe użytkowy: https://github.com/zeroturnaround/zt-zip

Oto przykład z skompresowanie wszystkich plików wewnątrz katalogu:

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

bardzo wygodne.

1

Nieco zmodyfikowana wersja (krótszy) korzystając 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() 
} 
Powiązane problemy