What signature can I use to download files using Axum and Tokio?

I'm using axum and this code (found here) to download files:

use axum::{
    body::StreamBody,
    http::{header, StatusCode},
    response::{Headers, IntoResponse},
    routing::get,
    Router,
};
use std::net::SocketAddr;
use tokio_util::io::ReaderStream;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(handler));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn handler() -> impl IntoResponse {
    // `File` implements `AsyncRead`
    let file = match tokio::fs::File::open("Cargo.toml").await {
        Ok(file) => file,
        Err(err) => return Err((StatusCode::NOT_FOUND, format!("File not found: {}", err))),
    };

    // convert the `AsyncRead` into a `Stream`
    let stream = ReaderStream::new(file);

    // convert the `Stream` into an `axum::body::HttpBody`
    let body = StreamBody::new(stream);

    let headers = Headers([
        (header::CONTENT_TYPE, "text/toml; charset=utf-8"),
    ]);

    Ok((headers, body))
}

Everything works. But I cannot find a way to move the below code in a separate function:

let file = match tokio::fs::File::open("Cargo.toml").await {
    Ok(file) => file,
    Err(err) => return Err((StatusCode::NOT_FOUND, format!("File not found: {}", err))),
};

I would like to use both tokio::fs::File and https://crates.io/crates/rust-s3 methods in this function.

So I need a "common type" which appear to be AsyncRead, I think.

What should be the signature of the function?

I tried with:

use tokio::io::AsyncRead;

pub struct Player {
    db: Arc<DB>
}

impl Handler {
    pub async fn player_pdf(
        &self,
        id: &str,
    ) -> Result<&(dyn AsyncRead)> {
        //...use id here...

        let file = &tokio::fs::File::open("player.pdf").await?;

        Ok(file)
    }
}

but I get the error:

error[E0308]: mismatched types
    |
55  |         Ok(file)
    |         -- ^^^^
    |         |  |
    |         |  expected reference, found struct `tokio::fs::File`
    |         |  help: consider borrowing here: `&file`
    |         arguments to this enum variant are incorrect
    |
    = note: expected reference `&dyn tokio::io::AsyncRead`
                  found struct `tokio::fs::File`

I tried with: let file = &tokio::fs::File::open("player.pdf").await?; and I got:

error[E0515]: cannot return value referencing temporary value
   |
43 |         let file = &tokio::fs::File::open(...
   |                     --------------------------- temporary value created here
...
55 |         Ok(file)
   |         ^^^^^^^^ returns a value referencing data owned by the current function

What can I use?

Use Result<Box<dyn AsyncRead>> instead.

1 Like

Thank you for the answer.

I think I better asked this here: Upload and download with Axum, streaming.

Can you help me? Thanks.