使用 Axum 框架与 MinIO 对象存储交互

使用 Axum 框架与 MinIO 对象存储交互

Axum 是一个用 Rust 编写的现代化、模块化、以路由为核心的 Web 框架,由 Tokio 团队(也是 hyper 和 tower 的维护者)开发,主打特点是:

类型安全:通过 Rust 的强类型系统,在编译期尽可能捕获错误。

与 Tower 紧密集成:Axum 使用 tower 生态来实现中间件(middlewares)、服务(services)等。

基于 Hyper:Axum 使用 Hyper 做 HTTP 底层处理,性能优秀。

异步友好:天然支持 Tokio 异步运行时,适合高并发场景。

创建项目

1
cargo init

/Cargo.toml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[package]
name = "minio-rs"
version = "0.1.0"
edition = "2024"

[dependencies]
axum = { version = "0.8.4", features = ["multipart"] }
config = "0.15.11"
dotenvy = "0.15.7"
tokio = { version = "1.46.1", features = ["full", "tracing"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
minio = "0.3.0"
futures-util = "0.3.31"

src/config/mod.rs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use serde::Deserialize;

#[derive(Debug, Deserialize, Clone)]
pub struct AppConfig {
pub server_address: String,
pub minio_endpoint: String,
pub minio_access_key: String,
pub minio_secret_key: String,
pub minio_bucket_name: String,
}

impl AppConfig {
pub fn from_env() -> Result<Self, config::ConfigError> {
let config = config::Config::builder()
.add_source(config::Environment::default())
.build()?;
config.try_deserialize()
}
}

注意:
请自行创建.env文件并写入以下环境变量:

1
2
3
4
5
server_address=
minio_endpoint=
minio_access_key=
minio_secret_key=
minio_bucket_name=

上传文件和下载文件的接口

src/main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use axum::{
Json, Router,
body::Bytes,
extract::{Multipart, Path, State},
http::StatusCode,
routing::{get, post},
};
use minio::s3::{Client, creds::StaticProvider, http::BaseUrl, types::S3Api};
use tracing::info;

mod config;

#[derive(Clone)]
pub struct AppState {
pub minio_client: minio::s3::Client,
pub minio_bucket_name: String,
}

#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();

dotenvy::dotenv().ok();

let config = config::AppConfig::from_env().unwrap();

let minio_endpoint: BaseUrl = config.minio_endpoint.parse().unwrap();

let static_provider =
StaticProvider::new(&config.minio_access_key, &config.minio_secret_key, None);

let client = Client::new(minio_endpoint, Some(Box::new(static_provider)), None, None);

if let Ok(client) = client {
let bucket = client
.bucket_exists(&config.minio_bucket_name)
.send()
.await
.unwrap();

if !bucket.exists {
info!("MinIO bucket does not exist, creating...");
client
.create_bucket(&config.minio_bucket_name)
.send()
.await
.unwrap();
}

info!("MinIO connect successfully.");

let app_state = AppState {
minio_client: client,
minio_bucket_name: config.minio_bucket_name,
};

let app = Router::new()
.route("/", get(hello_minio))
.route("/file", post(upload_file))
.route("/file/{object_key}", get(download_file))
.with_state(app_state);

let listener = tokio::net::TcpListener::bind(&config.server_address)
.await
.unwrap();

tracing::info!("listening on {}", &config.server_address);

axum::serve(listener, app).await.unwrap();
}
}

async fn hello_minio() -> (StatusCode, Json<String>) {
(StatusCode::OK, Json("hello minio".to_owned()))
}

async fn upload_file(state: State<AppState>, mut payload: Multipart) -> (StatusCode, Json<String>) {
let field = payload.next_field().await.unwrap().unwrap();
let name = field.name().unwrap().to_string();
if name.eq("file") {
let file_name = field.file_name().unwrap().to_string();
let byte = field.bytes().await.unwrap();
state
.minio_client
.put_object(&state.minio_bucket_name, file_name, byte.into())
.send()
.await
.unwrap();

(StatusCode::OK, Json("OK".to_string()))
} else {
(
StatusCode::FORBIDDEN,
Json("does not find file key's value".to_string()),
)
}
}

async fn download_file(
state: State<AppState>,
Path(object_key): Path<String>,
) -> (StatusCode, Bytes) {
let object = state
.minio_client
.get_object(&state.minio_bucket_name, object_key)
.send()
.await
.unwrap();
let bytes = object
.content
.to_segmented_bytes()
.await
.unwrap()
.to_bytes();
return (StatusCode::OK, bytes);
}

完整代码 Github 仓库:https://github.com/1uciuszzz/axum-minio-example

使用 Axum 框架与 MinIO 对象存储交互

https://1uciuszzz.github.io/2025/07/11/rust-axum-and-minio-example/

作者

1uciuszzz

发布于

2025-07-11

更新于

2025-07-11

许可协议

评论