You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:30:49