在 Web 应用中,我们通常需要提供静态资源,如 HTML、CSS、图片、客户端 JavaScript 文件等。Express 提供了一个内置中间件 express.static,用于轻松托管静态文件。本章将详细介绍其用法、配置和最佳实践。
静态文件是指内容不会动态改变的文件,比如样式表(CSS)、图像(JPEG、PNG)、前端脚本(JS)等。当客户端请求这些资源时,服务器只需读取文件并返回其内容,无需任何业务逻辑处理。提供静态文件服务是 Web 服务器的基本功能之一。
express.static 是 Express 唯一保留的内置中间件,它基于 serve-static 模块,负责托管静态资源。使用时,只需将包含静态资源的目录名称传递给该中间件。
假设你的项目结构如下:
my-express-app/
├── public/
│ ├── css/
│ │ └── style.css
│ ├── images/
│ │ └── logo.png
│ └── js/
│ └── main.js
└── app.js
在 app.js 中添加:
const express = require('express');
const app = express();
app.use(express.static('public'));
app.listen(3000, () => console.log('Server running on port 3000'));
现在,你可以通过以下 URL 访问这些文件:
http://localhost:3000/css/style.csshttp://localhost:3000/images/logo.pnghttp://localhost:3000/js/main.js注意,URL 中不需要包含 public 目录名,因为 express.static 将 public 目录映射到了根路径。
如果你有多个静态资源目录,可以多次调用 express.static。Express 会按照中间件的顺序依次查找文件,一旦找到匹配的文件就停止搜索。
app.use(express.static('public'));
app.use(express.static('assets'));
当请求 /images/logo.png 时,Express 会先在 public 目录中查找,如果找不到,再到 assets 目录中查找。这种机制允许你为不同来源的静态资源设置优先级。
有时我们希望静态文件不要直接挂载在根路径下,而是有一个前缀,例如 /static。这可以通过给 express.static 指定挂载路径来实现:
app.use('/static', express.static('public'));
现在,文件的访问 URL 变为:
http://localhost:3000/static/css/style.csshttp://localhost:3000/static/images/logo.png虚拟路径有助于组织路由,避免与动态路由冲突。
在上面的例子中,'public' 是相对路径,相对于启动 Node.js 进程的工作目录。为了更可靠,建议使用绝对路径,结合 path 模块:
const path = require('path');
app.use('/static', express.static(path.join(__dirname, 'public')));
这样无论从哪个目录启动应用,都能正确找到 public 文件夹。
express.static 接受一个可选的对象作为第二个参数,用于自定义行为。常用选项:
maxAge:设置缓存控制头的 max-age(单位毫秒)。例如 maxAge: '7d' 或 maxAge: 1000 * 60 * 60 * 24 * 7。dotfiles:是否允许提供点文件(如 .htaccess),可选 'allow'、'deny'、'ignore'(默认)。etag:是否生成 ETag 头(默认 true)。lastModified:是否设置 Last-Modified 头(默认 true)。setHeaders:自定义响应头的函数。示例:
app.use('/static', express.static('public', {
maxAge: '1d', // 客户端缓存一天
setHeaders: (res, path, stat) => {
if (path.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache');
}
}
}));
express.static 内置了安全机制,防止目录遍历攻击(如请求 /static/../app.js)。它会自动解析路径并确保文件位于指定的根目录内。因此,你无需额外担心路径遍历漏洞。
但是,仍需注意以下几点:
下面是一个完整的 Express 应用,演示了静态文件服务与动态路由的共存:
const express = require('express');
const path = require('path');
const app = express();
// 托管静态文件,并设置缓存
app.use('/static', express.static(path.join(__dirname, 'public'), {
maxAge: '7d',
immutable: true // 适用于 hash 命名的文件
}));
// 动态路由
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<h1>Hello World</h1>
<img src="/static/images/logo.png" alt="Logo">
<script src="/static/js/main.js"></script>
</body>
</html>
`);
});
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
在此例中,动态路由 / 返回的 HTML 页面中引用了静态资源,这些资源由 /static 前缀提供。
maxAge,减少服务器负载。main.a1b2c3.js),并设置 immutable: true,实现永久缓存。/static)清晰区分。express.static 是 Express 提供的一个简单而强大的中间件,能够快速搭建静态文件服务。通过合理配置和遵循最佳实践,你可以在开发和生产环境中高效地托管静态资源。