2012-04-03 39 views

Trả lời

16

Hiện chưa cách nào để làm điều này loại điều từ thư viện Scala tiêu chuẩn, nhưng nó khá dễ sử dụng 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() 
} 

Tôi đang tập trung vào sự đơn giản thay vì hiệu quả ở đây (không kiểm tra lỗi và đọc và viết một byte tại một thời điểm không phải là lý tưởng), nhưng nó hoạt động, và có thể dễ dàng được cải thiện.

+0

Bạn nên 'in.close() 'khi bạn đã hoàn tất với' in'. – leedm777

+0

Vâng, tất nhiên — tôi đã sửa nó ngay bây giờ. –

+0

Và tôi biết nó không được tối ưu hóa, nhưng không cần phải bọc byte trong một mảng. Bạn có thể chỉ đơn giản là 'zip.write (b)'. – leedm777

3

Đây là thêm một chút phong cách scala trong trường hợp bạn muốn chức năng:

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

và đừng quên nhập khẩu:

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

'readByte (in) .takeWhile (_> -1) .toList' sẽ tiêu tốn rất nhiều bộ nhớ trong khi đọc tệp lớn. Sử dụng 'Iterator' có thể tốt hơn. – jilen

+0

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

3

Câu trả lời Travis là đúng nhưng tôi đã tinh chỉnh một ít để có phiên bản mã nhanh hơn của mình:

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

Gần đây, tôi phải làm việc với các tệp zip quá và thấy tiện ích này rất đẹp: https://github.com/zeroturnaround/zt-zip

Dưới đây là một ví dụ về nén tất cả các file bên trong một thư mục:

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

Rất thuận tiện.

1

Một chút sửa đổi (ngắn hơn) phiên bản sử dụng 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() 
} 
Các vấn đề liên quan