2017-10-02 21 views
7

Çok fazla RAM kullanmadan gzipped tar dosyası oluşturmaya çalışıyorum. Yapmak istediğim şeyin Bash eşdeğerdir: ben hem onlar aktarımı destekleyen demek tar ve flate2 kütüphaneleri, kullanıyorumÇok fazla RAM kullanmadan gzipped tar dosyası nasıl oluşturulur?

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

. Birini diğerine nasıl aktarabileceğimi anlayamıyorum. Write uygulamacılarına bakmayı denedim, ancak gereksinimlerime uyan bir akış türü görmüyorum.

Geçerli uygulamam istenen çıktıya (yani .tar.gz dosyası) sahiptir, ancak özellikle dosya boyutu büyük olduğunda çok fazla RAM kullanır. Oluşturulan dosya ayrıca giriş boyutu büyük olduğunda "katran: arşivde beklenmedik EOF" verir, ancak küçük girişler için iyidir. Bu bana, Bash'in istediği gibi akışları pipetlemediğini gösteriyor.

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()?; 

cevap

8

Eğer RAM kullanıyor anlamak ve tar büyük dosyalar için bir hata bildirir neden senin kod yaptıklarına tam anlayalım için:


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

Builder::new belgelerine baktığımızda biz ana sorunu zaten görebiliyor: "Yazılan tüm verilerin hedefi olarak alttaki nesneyle yeni bir arşiv oluşturucu oluştur". Vec'u (Write uygular) geçtiğiniz için, tar sıkıştırılmış tüm verilerin hedefleri vektöre yazılır. Ama vektör RAM'de saklanır.

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

Bu satır zaten dosyaları vektöre sıkıştırır, bu nedenle bu satırda RAM doldurur.

atlanıyor birkaç satır:

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

Burada sadece doldurulmuş vektör yazmak için kodlayıcı söyle. Ama, hatırlamak önemlidir, Write::write() ne kadar veri yazılır garanti yok! Daha güvenilir olan daha yüksek seviyeli fonksiyonlar için daha düşük seviyeli bir yapı taşıdır. Tüm veriler yazılana kadar tekrar tekrar write()'u arayacak olan write_all()'u kullanmak istiyorsunuz. Dolayısıyla, sadece write() kullanıyor olduğunuzdan, verilerin yalnızca bir kısmı yazılır. Çok az veri olduğunda, genellikle bir kerede yazılabilir, ancak daha fazla veriye sahip olduğunuzda, hata fark edilir hale gelir.


Bunun yerine ne yapmalı? Basit: Builder::new(), Write'u uygulayan bir şey bekler ve bunu hedef olarak kullanır. Ancak, tarencoder, Write uygular. Böylece, bu işe yarar:

// 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

Bu, "encoder", "Builder :: new (encoder)" öğesine taşındığından, ancak daha sonra "encoder.finish()") için kullanıldığından derleme yapmaz. –

+3

@NicolasChan Gosh, üzgünüm! Bunu test edemedim ve beynim henüz geçerli bir Rust derleyicisi değil: cevabımı düzenledim, kod şimdi çalışmalı. –

İlgili konular