是否可以从文件创建流而不是将文件内容加载到内存中?

时间:2019-12-20 16:07:32

标签: rust

我当前正在使用rusoto_s3库将文件上传到S3。我发现的所有示例都做同样的事情:打开文件,将文件的全部内容读入内存(Vec<u8>),然后将Vec转换为ByteStream(实现{{1} }。这是一个代码示例:

From<Vec<u8>>

对于小文件来说,这可能是可以接受的,但是(我认为)一旦您尝试上传大小大于可用堆内存的文件,此技术就会失效。

创建fn upload_file(&self, file_path: &Path) -> FileResult<PutObjectOutput> { let mut file = File::open(file_path)?; let mut file_data: Vec<u8> = vec![]; file.read_to_end(&mut file_data)?; let client = S3Client::new(Region::UsEast1); let mut request = PutObjectRequest::default(); request.body = Some(file_data.into()); Ok(client.put_object(request).sync()?) } 的另一种方法是using this initializer,它接受​​实现ByteStream特征的对象。我假设Stream将实现此特征,但事实并非如此。

我的问题:

是否可以通过实现File的{​​{1}}来构造某种类型?是使我自己的元组结构封装File并自己实现Stream的正确解决方案,这种实现是微不足道的吗?还有我看不到的另一种解决方案,还是我只是误解了上面代码中的内存分配方式?

1 个答案:

答案 0 :(得分:4)

  

是否可以通过实现Stream的文件构造某种类型?

不幸的是,没有。 stdfuturestokio中内置的功能目前无法直接执行。

由于Stream项的“分派”性质,这种实现将不得不为传入数据的每个切片分配一个新的拥有的缓冲区,并将其移交给调用方。那不是很有效。直到Rust语言具有通用关联类型(GAT)(希望在明年)之前,我们才能令人满意地解决该问题。请查看this futures-rs ticketNiko's async interview #2以获得更多详细信息。

话虽这么说,但现在在一些用例中,需要在底层IO上使用Stream构面并且足够好。

  

制作我自己的元组结构(包装File并实现Stream本身)的正确解决方案是正确的吗?

对于futures-0.1所依赖的rusoto,有几种实现方法:

  • 实现Stream特征,用于包装Read的结构
  • 使用futures等实用程序功能,例如futures::stream::poll_fn
  • tokio-codec-0.1具有出色的FramedRead,它已经实现了Stream

第三个肯定是最简单的:

use futures::stream::Stream;  // futures = "0.1.29"
use rusoto_core::{ByteStream, Region};  // rusoto_core = "0.42.0"
use rusoto_s3::{PutObjectOutput, PutObjectRequest, S3Client, S3};  // rusoto_s3 = "0.42.0"
use std::{error::Error, fs::File, path::Path};
use tokio_codec::{BytesCodec, FramedRead};  // tokio-codec = "0.1.1"
use tokio_io::io::AllowStdIo;  // tokio-io = "0.1.12"

fn upload_file(file_path: &Path) -> Result<PutObjectOutput, Box<dyn Error>> {
    let file = File::open(file_path)?;
    let aio = AllowStdIo::new(file);
    let stream = FramedRead::new(aio, BytesCodec::new()).map(|bs| bs.freeze());

    let client = S3Client::new(Region::UsEast1);
    let mut request = PutObjectRequest::default();
    request.body = Some(ByteStream::new(stream));

    Ok(client.put_object(request).sync()?)
}