静态文件 #
Web应用通常需要提供静态资源服务,如CSS、JavaScript、图片等。Rocket提供了简单的方式来处理静态文件。
配置静态文件服务 #
目录结构 #
text
my_app/
├── Cargo.toml
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── main.js
│ ├── images/
│ │ └── logo.png
│ └── favicon.ico
└── src/
└── main.rs
基本配置 #
rust
use rocket::fs::{FileServer, relative};
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![index])
.mount("/static", FileServer::from(relative!("static")))
}
FileServer配置 #
基本用法 #
rust
use rocket::fs::FileServer;
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/public", FileServer::from("static/"))
}
访问 /public/css/style.css 会返回 static/css/style.css 文件。
多个静态目录 #
rust
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/css", FileServer::from("static/css"))
.mount("/js", FileServer::from("static/js"))
.mount("/images", FileServer::from("static/images"))
}
相对路径 #
rust
use rocket::fs::relative;
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/static", FileServer::from(relative!("static")))
}
relative! 宏会相对于当前文件计算路径。
文件下载 #
NamedFile #
rust
use rocket::fs::NamedFile;
#[get("/download/<filename>")]
async fn download(filename: &str) -> Option<NamedFile> {
NamedFile::open(format!("files/{}", filename)).await.ok()
}
带Content-Type #
rust
use rocket::fs::NamedFile;
use rocket::http::ContentType;
#[get("/download/pdf")]
async fn download_pdf() -> Option<(ContentType, NamedFile)> {
let file = NamedFile::open("files/document.pdf").await.ok()?;
Some((ContentType::PDF, file))
}
强制下载 #
rust
use rocket::fs::NamedFile;
use rocket::http::Header;
use rocket::response::Responder;
#[get("/download/force/<filename>")]
async fn force_download(filename: &str) -> Option<(NamedFile, Header<'static>)> {
let file = NamedFile::open(format!("files/{}", filename)).await.ok()?;
let header = Header::new(
"Content-Disposition",
format!("attachment; filename=\"{}\"", filename)
);
Some((file, header))
}
Content-Type处理 #
常用Content-Type #
| 类型 | MIME |
|---|---|
| HTML | text/html |
| CSS | text/css |
| JavaScript | application/javascript |
| JSON | application/json |
| PNG | image/png |
| JPEG | image/jpeg |
| GIF | image/gif |
| SVG | image/svg+xml |
| application/pdf | |
| ZIP | application/zip |
自定义Content-Type #
rust
use rocket::fs::NamedFile;
use rocket::http::ContentType;
#[get("/file/<filename>")]
async fn get_file(filename: &str) -> Option<(ContentType, Vec<u8>)> {
let content = std::fs::read(format!("files/{}", filename)).ok()?;
let content_type = match filename.rsplit('.').next() {
Some("pdf") => ContentType::PDF,
Some("png") => ContentType::PNG,
Some("jpg") | Some("jpeg") => ContentType::JPEG,
_ => ContentType::Binary,
};
Some((content_type, content))
}
静态文件缓存 #
设置缓存头 #
rust
use rocket::fs::NamedFile;
use rocket::http::Header;
#[get("/cached/<filename>")]
async fn cached_file(filename: &str) -> Option<(NamedFile, Header<'static>)> {
let file = NamedFile::open(format!("static/{}", filename)).await.ok()?;
let cache_header = Header::new("Cache-Control", "public, max-age=31536000");
Some((file, cache_header))
}
favicon处理 #
提供favicon #
rust
use rocket::fs::NamedFile;
#[get("/favicon.ico")]
async fn favicon() -> Option<NamedFile> {
NamedFile::open("static/favicon.ico").await.ok()
}
完整示例 #
rust
#[macro_use] extern crate rocket;
use rocket::fs::{FileServer, NamedFile, relative};
use rocket::http::{ContentType, Header};
use rocket_dyn_templates::Template;
use rocket::serde::Serialize;
#[derive(Serialize)]
struct IndexContext {
title: String,
}
#[get("/")]
fn index() -> Template {
Template::render("index", &IndexContext {
title: "Home".to_string(),
})
}
#[get("/favicon.ico")]
async fn favicon() -> Option<NamedFile> {
NamedFile::open(relative!("static/favicon.ico")).await.ok()
}
#[get("/download/<filename>")]
async fn download(filename: &str) -> Option<(ContentType, NamedFile, Header<'static>)> {
let file = NamedFile::open(format!("files/{}", filename)).await.ok()?;
let content_type = match filename.rsplit('.').next() {
Some("pdf") => ContentType::PDF,
Some("png") => ContentType::PNG,
Some("jpg") | Some("jpeg") => ContentType::JPEG,
_ => ContentType::Binary,
};
let header = Header::new(
"Content-Disposition",
format!("attachment; filename=\"{}\"", filename)
);
Some((content_type, file, header))
}
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(Template::fairing())
.mount("/", routes![index, favicon, download])
.mount("/static", FileServer::from(relative!("static")))
}
HTML模板引用静态文件 #
html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/css/style.css">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
</head>
<body>
<img src="/static/images/logo.png" alt="Logo">
<script src="/static/js/main.js"></script>
</body>
</html>
最佳实践 #
目录组织 #
text
static/
├── css/
│ ├── main.css
│ └── components/
│ ├── navbar.css
│ └── card.css
├── js/
│ ├── main.js
│ └── utils/
│ └── helpers.js
├── images/
│ ├── logo.png
│ └── icons/
│ ├── home.svg
│ └── user.svg
└── fonts/
└── custom.woff2
缓存策略 #
| 文件类型 | 缓存时间 |
|---|---|
| HTML | 不缓存或短时间 |
| CSS/JS | 长期缓存+版本号 |
| 图片 | 长期缓存 |
| 字体 | 长期缓存 |
版本化静态资源 #
html
<link rel="stylesheet" href="/static/css/style.css?v=1.0.0">
<script src="/static/js/main.js?v=1.0.0"></script>
下一步 #
掌握了静态文件服务后,让我们继续学习 请求守卫,了解如何实现请求级别的权限控制。
最后更新:2026-03-28