如何将future :: Stream写入磁盘而不先将其完全存储在内存中?

时间:2018-11-11 02:46:49

标签: stream rust future

这里有一个使用Rusoto S3下载文件的示例: How to save a file downloaded from S3 with Rusoto to my hard drive?

问题在于它看起来像是将整个文件下载到内存中,然后将其写入磁盘,因为它使用write_all方法,该方法需要一个字节数组,而不是一个流。如何使用实现了StreamingBodyfutures::Stream将文件流式传输到磁盘?

1 个答案:

答案 0 :(得分:0)

由于StreamingBody实现了Stream<Item = Vec<u8>, Error = Error>,因此我们可以构造一个MCVE来表示:

extern crate futures; // 0.1.25

use futures::{prelude::*, stream};

type Error = Box<std::error::Error>;

fn streaming_body() -> impl Stream<Item = Vec<u8>, Error = Error> {
    const DUMMY_DATA: &[&[u8]] = &[b"0123", b"4567", b"89AB", b"CDEF"];
    let iter_of_owned_bytes = DUMMY_DATA.iter().map(|&b| b.to_owned());
    stream::iter_ok(iter_of_owned_bytes)
}

然后我们可以通过某种方式获得“流主体”,并使用Stream::for_each处理Stream中的每个元素。在这里,我们仅使用提供的某些输出位置调用write_all

use std::{fs::File, io::Write};

fn save_to_disk(mut file: impl Write) -> impl Future<Item = (), Error = Error> {
    streaming_body().for_each(move |chunk| file.write_all(&chunk).map_err(Into::into))
}

然后我们可以写一些测试主体:

fn main() {
    let mut file = Vec::new();

    {
        let fut = save_to_disk(&mut file);
        fut.wait().expect("Could not drive future");
    }

    assert_eq!(file, b"0123456789ABCDEF");
}

有关此简单实施质量的重要说明:

  1. write_all的调用可能会阻塞,您不应在异步程序中执行此操作。最好将阻塞工作移交给线程池。

  2. 使用Future::wait会强制线程阻塞直到将来完成为止,这对测试很有用,但对于您的实际用例而言可能并不正确。

另请参阅: