如何将超响应正文写入文件?

bro*_*hjt 6 hyper rust-tokio

我正在尝试使用 tokio 编写一个测试程序,该程序从网站上获取文件并将流式响应写入文件。超级网站显示了一个示例,该示例使用 while 循环并使用.data()响应主体的方法,但我想与.map()其他几个人一起操作流。

我认为下一个合理的尝试是AsyncRead使用.into_async_read()from 方法将流转换为 an TryStreamExt,但这似乎不起作用。我不得不使用地图将 the 转换hyper::error::Error为 astd::error::Error以获取 a TryStream,但现在编译器告诉我AsyncRead没有为转换后的流实现。这是我的 main.rs 文件和错误:

src/main.rs

use futures::stream::{StreamExt, TryStreamExt};
use http::Request;
use hyper::{Body, Client};
use hyper_tls::HttpsConnector;
use tokio::fs::File;
use tokio::io;

use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let https = HttpsConnector::new();
    let client = Client::builder().build::<_, Body>(https);

    let request = Request::get("some file from the internet").body(Body::empty())?;
    let response = client.request(request).await?;

    let mut stream = response
        .body()
        .map(|result| result.map_err(|error| std::io::Error::new(std::io::ErrorKind::Other, "Error!")))
        .into_async_read();
    let mut file = File::create("output file").await?;

    io::copy(&mut stream, &mut file).await?;

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

错误

error[E0277]: the trait bound `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure@src/main.rs:20:14: 20:103]>>: tokio::io::async_read::AsyncRead` is not satisfied
  --> src/main.rs:24:5
   |
24 |     io::copy(&mut stream, &mut file).await?;
   |     ^^^^^^^^ the trait `tokio::io::async_read::AsyncRead` is not implemented for `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure@src/main.rs:20:14: 20:103]>>`
   | 
  ::: /Users/jackson/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.13/src/io/util/copy.rs:63:12
   |
63 |         R: AsyncRead + Unpin + ?Sized,
   |            --------- required by this bound in `tokio::io::util::copy::copy`

error[E0277]: the trait bound `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure@src/main.rs:20:14: 20:103]>>: tokio::io::async_read::AsyncRead` is not satisfied
  --> src/main.rs:24:5
   |
24 |     io::copy(&mut stream, &mut file).await?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `tokio::io::async_read::AsyncRead` is not implemented for `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure@src/main.rs:20:14: 20:103]>>`
   |
   = note: required because of the requirements on the impl of `core::future::future::Future` for `tokio::io::util::copy::Copy<'_, futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure@src/main.rs:20:14: 20:103]>>, tokio::fs::file::File>`
Run Code Online (Sandbox Code Playgroud)

Han*_*ger 4

你几乎已经拥有了。您调用了futures::io::AsyncReadinto_async_read的实现,但您想要tokio::io::AsyncRead

tokio-util箱为您提供了执行此转换的工具。

添加到您的Cargo.toml

tokio-util = { version = "0.3.1", features=["compat"] }
Run Code Online (Sandbox Code Playgroud)

假设您添加了一个如下所示的转换函数:

tokio-util = { version = "0.3.1", features=["compat"] }
Run Code Online (Sandbox Code Playgroud)

那么你的代码可能会变成:

fn to_tokio_async_read(r: impl futures::io::AsyncRead) -> impl tokio::io::AsyncRead {
    tokio_util::compat::FuturesAsyncReadCompatExt::compat(r)
}
Run Code Online (Sandbox Code Playgroud)