我正在尝试创建一个gzipped tar文件而不占用大量内存。 Bash相当于我想做的事情:
tar -cf - -C $INPUT . | gzip -cv - > $OUTPUT
我正在使用tar和flate2库,两者都表示他们支持流媒体。我无法弄清楚如何将一个流式传输到另一个。我曾尝试查看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()?;
答案 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()?;