Menu

express.static:在 Express 中提供静态文件

app.use(express.static(path.join(__dirname, 'public'))) 会在对应的 URL 上提供 public 文件夹中的每个文件:public/style.css 响应 /style.css。加上挂载路径,就能把它们放在某个前缀下。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

express.static 是用来提供某个文件夹中文件的内置中间件。app.use(express.static(path.join(__dirname, 'public'))) 让 public 中的每个文件都能通过 URL 中相同的路径访问:public/style.css 响应 /style.css,public/index.html 响应 /。Express 会根据文件扩展名设置 Content-Type;没有匹配的文件时,它会把请求交给你的路由,而不是返回 404。

GET /style.css 的响应就是这个文件的文本,在“标头”标签页中可以看到 Content-Type: text/css; charset=utf-8。发送 GET / 会得到 index.html,再发送 GET /api/time:没有这个文件,所以由路由来响应。

为什么文件夹路径需要 __dirname

express.static('public') 会相对于你启动 Node 时所在的文件夹解析 'public',而不是 server.js 所在的文件夹。在项目目录里运行 node server.js 时没问题,但如果进程管理器或某个脚本从别的地方启动应用,就会出错:

// project/server.js uses express.static('public')
// cd project && node server.js      -> serves project/public
// node project/server.js            -> looks for ./public, every file is 404

path.join(__dirname, 'public') 指的是包含这行代码的文件旁边的那个文件夹,无论 Node 从哪里启动。在 ES 模块中,使用 path.join(import.meta.dirname, 'public')(Node 20.11 及以上)。__dirname 和 path.join 的详细内容见 path 模块页面。

在前缀下提供文件

先传入一个挂载路径,就能把文件夹放在某个 URL 前缀下。这个前缀不属于文件在磁盘上的路径:

GET /assets/app.js 以 text/javascript 返回 console.log("hi");,GET /assets/img/dot.svg 则能访问子文件夹。不带前缀发送 GET /app.js:结果是 404,因为这些文件只存在于 /assets 下。用前缀可以避免文件 URL 和你的 API 路由冲突。

用 maxAge 设置缓存头

默认情况下,Express 发送 Cache-Control: public, max-age=0,所以浏览器每次使用文件前都会向服务器确认(借助 ETag 响应头,文件没变时会很快得到 304 Not Modified)。对于每次构建都会改名的文件,比如 app.3f9a1c.js,可以让浏览器把它们缓存下来:

打开“标头”标签页:Cache-Control 现在是 public, max-age=604800,也就是以秒计的七天。index: false 关闭了对文件夹 URL 提供 index.html 的功能。其他有用的选项还有 extensions: ['html'],它让 /about 能找到 about.html;以及 dotfiles,默认值是 'ignore',所以 .env 这样的文件永远不会被提供出去。

单页应用:回退到 index.html

为客户端路由构建的 React 或 Vue 应用,需要每个未知路径都返回 index.html,这样在 /settings 上刷新页面时才不会 404。把这个回退放在 express.static 和你的 API 路由之后:

app.use(express.static(path.join(__dirname, 'dist')));
app.use('/api', apiRouter);

// Express 5 wildcard syntax
app.get('/*splat', (req, res) => {
    res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

常见错误

在 URL 中带上文件夹名。 使用 app.use(express.static('public')) 时,URL 是 /style.css,而不是 /public/style.css。如果想让 URL 中出现 /public,就挂载它:app.use('/public', express.static(...))。

在 HTML 中用相对路径链接文件。 在 /blog/post 页面上,<link href="style.css"> 请求的是 /blog/style.css。让路径以斜杠开头:/style.css。

把机密放进静态文件夹。 里面的所有内容都是公开的。不要把配置文件、密钥和源代码放进去。

常见问题

可以多次使用 express.static 吗?

可以。每个文件夹注册一个;Express 会按添加顺序在这些文件夹中查找,并提供它找到的第一个文件。如果几个文件夹里可能有同名文件,就给每个文件夹设置自己的挂载路径。

生产环境中应该让 Express 提供静态文件吗?

小型应用这样做没问题。流量很大时,在前面放一个 CDN 或 Nginx 这样的反向代理,它们提供文件更快,还能在离用户更近的地方缓存文件,而 Express 只负责 API。

如何在路由中发送某个特定文件?

使用 res.sendFile(path.join(__dirname, 'files', 'report.pdf'))。路径必须是绝对路径(或者你传入 root 选项)。如果希望浏览器保存文件而不是显示它,就改用 res.download()。

Coddy 编程语言插图

和 Coddy 一起学编程

开始