我正在尝试创建一个gzipped tar文件而不占用大量内存.我想要做的Bash相当于:
tar -cf - -C $INPUT . | gzip -cv - > $OUTPUT
Run Code Online (Sandbox Code Playgroud)
我正在使用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()?;
Run Code Online (Sandbox Code Playgroud)
Luk*_*odt 10
要了解为什么使用RAM以及为什么tar报告大文件的错误,让我们了解您的代码究竟在做什么:
let mut archive = Builder::new(Vec::new());
Run Code Online (Sandbox Code Playgroud)
查看Builder::new文档,我们已经可以看到主要问题:"创建一个新的归档构建器,其底层对象是所有写入数据的目标".由于您传递的是Vec(实现Write),所有tar压缩数据的目标将被写入向量.但是矢量存储在RAM中.
archive.append_dir_all("myfiles", "myfiles")?;
Run Code Online (Sandbox Code Playgroud)
这一行已经将文件压缩到向量中,因此在这一行中,RAM填满了.
跳过几行:
encoder.write(&archive.into_inner()?)?;
Run Code Online (Sandbox Code Playgroud)
在这里,您告诉编码器写下您刚填充的矢量.但是,重要的是要记住,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()?;
Run Code Online (Sandbox Code Playgroud)