2017-10-02 14 views
7

Tôi đang cố gắng tạo một tệp tar gzipped mà không cần nó chiếm nhiều RAM. Các Bash tương đương với những gì tôi muốn làm là:Làm thế nào để tạo một tệp tar gzipped mà không cần sử dụng nhiều RAM?

tar -cf - -C $INPUT . | gzip -cv - > $OUTPUT 

Tôi đang sử dụng tarflate2 thư viện, mà cả hai cho biết họ ủng hộ trực tuyến. Tôi không thể tìm ra cách để dòng một trong những khác. Tôi đã thử xem xét các triển khai Write, nhưng không thấy một loại luồng phù hợp với nhu cầu của tôi.

Triển khai hiện tại của tôi có đầu ra mong muốn (cụ thể là tệp .tar.gz), nhưng nó sử dụng nhiều RAM, đặc biệt khi kích thước tệp lớn. Tệp được tạo cũng cung cấp "tar: EOF không mong muốn trong kho lưu trữ" khi kích thước đầu vào lớn, nhưng tốt với đầu vào nhỏ. Điều này chỉ ra với tôi rằng nó không phải là đường ống các dòng như Bash sẽ.

use flate2::write::GzEncoder; 
use flate2::Compression; 
use std::fs::File; 
use tar::Builder; 

// Create tar archive 
let mut archive = Builder::new(Vec::new()); 
archive.append_dir_all("myfiles", "myfiles")?; 

// Gzip tar archive and write to file 
let compressed_file = File::create("backup.tar.gz")?; 
let mut encoder = GzEncoder::new(compressed_file, Compression::Default); 
encoder.write(&archive.into_inner()?)?; 
encoder.finish()?; 

Trả lời

8

Để hiểu lý do tại sao bạn đang sử dụng RAM và tại sao tar báo cáo lỗi cho các tập tin lớn, chúng ta hãy hiểu những gì chính xác mã của bạn được thực hiện:


let mut archive = Builder::new(Vec::new()); 

Nhìn vào các tài liệu Builder::new, chúng tôi có thể đã thấy vấn đề chính: "Tạo trình tạo kho lưu trữ mới với đối tượng bên dưới làm đích đến của tất cả dữ liệu được viết". Vì bạn đang đi qua một Vec (thực hiện Write), đích của tất cả dữ liệu được nén tar sẽ được ghi vào vectơ. Nhưng vector được lưu trữ trong RAM.

archive.append_dir_all("myfiles", "myfiles")?; 

Dòng này đã nén tệp vào vectơ, do đó, trong dòng này, RAM sẽ đầy.

Bỏ qua một vài dòng:

encoder.write(&archive.into_inner()?)?; 

Ở đây bạn nói với encoder để viết vector bạn chỉ cần điền. Nhưng, điều quan trọng cần nhớ là Write::write() không đảm bảo số lượng dữ liệu được ghi! Nó là một khối xây dựng cấp thấp hơn cho các chức năng cấp cao hơn đáng tin cậy hơn. Bạn muốn sử dụng write_all() thay vào đó sẽ liên tục gọi write() cho đến khi tất cả dữ liệu được ghi. Vì bạn chỉ sử dụng write(), chỉ một phần dữ liệu được ghi. Khi bạn có rất ít dữ liệu, nó thường có thể được viết tất cả cùng một lúc, nhưng một khi bạn có nhiều dữ liệu hơn, lỗi sẽ trở nên đáng chú ý.


Vậy phải làm gì? Đơn giản: Builder::new() hy vọng điều gì đó triển khai Write và sử dụng điểm đó làm đích đến. Nhưng tarencoder của bạn thực hiện Write. Do đó, điều này sẽ hoạt động:

// Create Gzip file 
let compressed_file = File::create("backup.tar.gz")?; 
let mut encoder = GzEncoder::new(compressed_file, Compression::Default); 

{ 
    // Create tar archive and compress files 
    let mut archive = Builder::new(&mut encoder); 
    archive.append_dir_all("myfiles", "myfiles")?; 
} 

// Finish Gzip file 
encoder.finish()?; 
+0

Điều này không biên dịch vì 'bộ mã hóa' được chuyển sang' Bộ dựng :: mới (bộ mã hóa) 'nhưng sau này được sử dụng trong' encoder.finish() '. –

+3

@NicolasChan Gosh, tôi xin lỗi! Tôi đã không thể kiểm tra nó, và bộ não của tôi không phải là một trình biên dịch Rust hợp lệ được nêu ra: o Tôi đã chỉnh sửa câu trả lời của tôi, mã sẽ hoạt động ngay bây giờ. –

Các vấn đề liên quan