如何在不使用大量RAM的情况下创建一个gzipped tar文件?

时间:2017-10-02 07:07:54

标签: rust

我正在尝试创建一个gzipped tar文件而不占用大量内存。 Bash相当于我想做的事情:

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

我正在使用tarflate2库,两者都表示他们支持流媒体。我无法弄清楚如何将一个流式传输到另一个。我曾尝试查看Write实现者​​,但没有看到符合我需求的流类型。

我当前的实现具有所需的输出(即.tar.gz文件),但它耗尽了大量的RAM,尤其是当文件大小很大时。当输入大小很大时,创建的文件也会提供“tar:存档中的意外EOF”,但输入较小时会很好。这告诉我,它不像Bash那样管道流。

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

1 个答案:

答案 0 :(得分:10)

要了解您使用RAM的原因以及tar报告大型文件错误的原因,请让我们了解您的代码究竟在做什么:

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

查看Builder::new文档,我们已经可以看到主要问题:"创建一个新的存档构建器,其中基础对象作为所有写入数据的目的地"。由于您传递的是Vec(实现Write),因此所有tar压缩数据的目标都将写入向量。但是矢量存储在RAM中。

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

这一行已经将文件压缩到向量中,所以在这一行中,RAM填满了。

跳过几行:

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

在这里,您告诉编码器写下您刚填充的矢量。 但是,重要的是要记住,Write::write()无法保证写入多少数据!它是更高级别功能的更低级别构建块,更可靠。您希望使用write_all()代替重复调用write(),直到写入所有数据。因此,由于您只使用write(),因此只会写入部分数据。如果数据非常少,通常可以一次性写入,但是一旦有了更多数据,错误就会变得明显。

那又该做什么呢?简单:Builder::new()期望实现Write的东西并将其用作目标。但是,tar encoder确实实施了Write。因此,这应该工作:

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