🦀

axumでファイルダウンロード処理

2024/09/02に公開

はじめに

プライベートでRustをコツコツ書いてます。

概要

axum(というかRust)でファイルダウンロード機能のサンプル等がググっても見つからなかったので、実装してみました。

簡単に機能だけざっくり説明すると、DBから取得したデータをJsonに変換し、jsonファイルとしてダウンロードさせるAPIです。

ライブラリ

axum

axumで適当にアプリ作ってるので、そのアプリに実装するため、使いました。

https://github.com/tokio-rs/axum

tokio_util

ストリームとして扱うのに、使用しました。

https://docs.rs/tokio-util/latest/tokio_util/

実装

処理

ざっくり下記になります。

  1. DBから取得したデータをres変数(戻り値はResult<Option<Vec<構造体>>>)に格納
  2. レスポンス用の構造体(Vec<JsonHistoriesDownload>)に変換
  3. レスポンス用の構造体をバイトベクターへシリアライズ
  4. Cursorにラップし、AsyncRead扱いにする
  5. ReadStreamに変換し、非同期のストリームにする
  6. レスポンスボディに設定
  7. ヘッダーにcontent-typeとdispositionを設定
  8. ヘッダーとボディをレスポンス

JSONファイルダウンロード

ストリームにするのに、tokio-utilが必要で、さらにDBから取得したデータをストリームに読み込ますのに、バイト列にする必要があり、Cursorを使いました。
※細かいエラーハンドリング等はプライベート開発なので、適当してます。

use tokio_util::io::ReaderStream;

pub async fn download_histories(
    Extension(modules): Extension<Arc<Modules>>,
    Path(id): Path<String>,
) -> Result<impl IntoResponse, StatusCode> {
    let res = modules.bank_manager_use_case().download_histories(id).await; // DBからデータ取得

    match res {
        Ok(dl_histories) => dl_histories
            .map(|data| {
                let json: Vec<JsonHistoriesDownload> = data.into_iter().map(|d| d.into()).collect();
                let json_data = serde_json::to_vec(&json).expect("error");
                let cursor = Cursor::new(json_data);

                let stream = ReaderStream::new(cursor);
                let body = Body::from_stream(stream);

                let mut headers = HeaderMap::new();
                headers.insert(header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
                headers.insert(
                    header::CONTENT_DISPOSITION,
                    HeaderValue::from_static("attachment; filename=\"deposit_histories.json\""),
                );

                (headers, body).into_response()
            })
            .ok_or_else(|| StatusCode::NOT_FOUND),
        Err(_) => {
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }

まとめ

tokio-utilが必要だったりと、完成するまで調査が必要でしたが、いろいろ勉強になりました。
もし、ファイルダウンロード実装したいときは、ご参考までに!

Discussion