如何将reqwest bytes_stream复制到tokio文件?解决AsyncRead适配问题
将reqwest字节流写入tokio文件的报错解决方法
问题描述
需要把reqwest下载的大文件写入tokio::fs::File,因文件过大无法存入内存,使用bytes_stream()而非bytes(),但执行tokio::io::copy时报错:
代码示例:
let mut tmp_file = tokio::fs::File::from(tempfile::tempfile()?); let byte_stream = reqwest::get(&link).await?.bytes_stream(); tokio::io::copy(&mut byte_stream, &mut tmp_file).await?;
错误信息:
| 153 | tokio::io::copy(&mut byte_stream, &mut tmp_file).await?; | --------------- ^^^^^^^^^^^^^^^^ the trait `tokio::io::AsyncRead` is not implemented for `impl Stream<Item = Result<bytes::bytes::Bytes, reqwest::Error>>` | | | required by a bound introduced by this call
解决方法
方法一:用tokio-util将Stream转为AsyncRead
reqwest的bytes_stream()返回的是Stream类型,而tokio::io::copy要求输入实现AsyncRead。可以借助tokio-util库的StreamReader将字节流包装成异步读取对象。
首先在Cargo.toml添加依赖:
tokio-util = { version = "0.7", features = ["codec"] }
修改后的代码:
use tokio_util::io::StreamReader; // ... let mut tmp_file = tokio::fs::File::from(tempfile::tempfile()?); let byte_stream = reqwest::get(&link).await?.bytes_stream(); // 将Stream包装为AsyncRead实现 let mut stream_reader = StreamReader::new(byte_stream); tokio::io::copy(&mut stream_reader, &mut tmp_file).await?;
该方案全程异步,适配大文件场景,是最推荐的解决方式。
方法二:手动遍历Stream写入文件
如果不想引入额外依赖,可以手动遍历字节流的每个数据块,逐个写入文件:
let mut tmp_file = tokio::fs::File::from(tempfile::tempfile()?); let byte_stream = reqwest::get(&link).await?.bytes_stream(); // 固定Stream以便遍历 tokio::pin!(byte_stream); while let Some(chunk) = byte_stream.next().await { let bytes = chunk?; tokio::io::write_all(&mut tmp_file, &bytes).await?; } // 可选:确保数据刷入磁盘 tmp_file.flush().await?;
逻辑简单直接,无需额外依赖,适合轻量场景。
方法三:先写入std::File再转tokio::fs::File(不推荐)
你提到的同步转异步方案确实可行,但会失去tokio异步IO的优势,大文件场景下可能阻塞线程,仅作为备选方案:
use std::io::Write; let mut std_file = tempfile::tempfile()?; let byte_stream = reqwest::get(&link).await?.bytes_stream(); // 同步写入(会阻塞当前线程) tokio::pin!(byte_stream); while let Some(chunk) = byte_stream.next().await { let bytes = chunk?; std_file.write_all(&bytes)?; } std_file.flush()?; // 将同步文件转为tokio异步文件 let mut tmp_file = tokio::fs::File::from(std_file); // 后续可对tmp_file执行异步操作
内容的提问来源于stack exchange,提问作者Qwertie
相关产品推荐
相关产品推荐

