我当前正在使用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
的正确解决方案,这种实现是微不足道的吗?还有我看不到的另一种解决方案,还是我只是误解了上面代码中的内存分配方式?
答案 0 :(得分:4)
是否可以通过实现Stream的文件构造某种类型?
不幸的是,没有。 std
,futures
或tokio
中内置的功能目前无法直接执行。
由于Stream项的“分派”性质,这种实现将不得不为传入数据的每个切片分配一个新的拥有的缓冲区,并将其移交给调用方。那不是很有效。直到Rust语言具有通用关联类型(GAT)(希望在明年)之前,我们才能令人满意地解决该问题。请查看this futures-rs
ticket和Niko'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()?)
}