静态文件 #

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
PDF 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