☰
Midway 静态资源(Static File)托管实战指南:Egg / Koa / Express / Serverless 四场景配置详解
2026/10/10 2:02:08 网站建设 项目流程
  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载

静态资源托管是 Web 应用中高频且基础的能力,用于将前端构建产物(js / css / html / png 等)以 HTTP 方式直接对外提供访问。本文以 Midway 官方文档(site/versioned_docs/version-2.0.0/static_file.md)为核心脉络,完整讲解 Midway 在@midwayjs/web(Egg)、@midwayjs/koa、@midwayjs/express以及 Serverless 四种场景下托管静态资源的标准做法,并结合当前仓库中@midwayjs/static-file组件的源码(packages/static-file)深入剖析其底层配置解析、缓存机制与多目录支持。读者读完本文,可以针对不同运行时快速落地一套可运行的静态资源方案,并理解每种方案在缓存、性能与平台差异上的取舍。

一、静态资源在 Midway 中的定位

静态资源泛指不需要服务端动态计算的资源文件,典型如前端框架打包后的js、css、图片、字体以及html入口文件。在 Midway 中,静态资源托管并不是框架核心内置的能力,而是依托各 Web 运行时自带的静态中间件方案来实现的:

  • Egg(@midwayjs/web)自带egg-static插件;
  • Koa(@midwayjs/koa)社区常用koa-static-cache中间件;
  • Express(@midwayjs/express)内置express.static;
  • Serverless(@midwayjs/faas)因网关不支持流式处理,需要选用支持 buffer 返回的中间件。

因此,选择哪套静态方案,取决于你当前使用的 Web 运行时。下面按场景逐一展开。

二、在 @midwayjs/web(Egg)中使用:egg-static 插件

Egg.js 默认提供了static插件,Midway 下只需要在插件配置中将其启用即可:

// src/config/plugin.ts exports.static = true;

egg-static插件基于koa-static-cache模块实现,因此它支持 koa-static-cache 的全部配置项。插件默认的 config 配置为:

{ prefix: '/public/', dir: path.join(appInfo.baseDir, 'app/public'), }
  • prefix:URL 路径前缀。例如文件放在${baseDir}/app/public/a.js,开启插件后,通过http://127.0.0.1:7001/public/a.js即可访问。
  • dir:静态文件在磁盘上的存放目录,默认指向应用目录下的app/public。

需要自定义配置时,在src/config/config.default.ts(或对应环境的配置文件中)覆盖static配置节即可,例如修改前缀、更换存放目录、调整缓存策略(maxAge、gzip等)。其余能力(动态加载、预加载、gzip 压缩、别名 alias 等)均透传至koa-static-cache。

三、在 @midwayjs/koa 中使用:koa-static-cache 中间件

Koa 场景与 Egg 类似,直接引入koa-static-cache模块即可:

$ npm i koa-static-cache --save

然后在src/configuration.ts中将静态中间件挂载到应用中。下面的示例把资源目录放在项目根目录下的public目录中:

// src/configuration.ts import { Configuration, App } from '@midwayjs/decorator'; import { Application } from '@midwayjs/koa'; import * as staticCache from 'koa-static-cache'; @Configuration() export class AutoConfiguration { @App() app: Application; async onReady() { this.app.use( staticCache({ prefix: '/public/', dir: path.join(this.app.getAppDir(), 'public'), }) ); } }

要点说明:

  • 通过@App()注入当前 Koa 应用实例,this.app.getAppDir()可拿到项目根目录,保证dir路径在不同工作目录下依然正确;
  • 中间件在onReady生命周期中注册,确保在应用启动、路由生效前完成挂载;
  • prefix与dir配合决定了「URL 前缀 → 磁盘目录」的映射关系,例如prefix: '/public/'时,public/logo.png通过/public/logo.png访问。

四、在 @midwayjs/express 中使用:express.static

Express 本身内置了静态资源支持,无需额外安装中间件,直接在src/configuration.ts中加入即可:

// src/configuration.ts import { Configuration, App } from '@midwayjs/decorator'; import { Application } from '@midwayjs/express'; import * as express from 'express'; @Configuration() export class AutoConfiguration { @App() app: Application; async onReady() { this.app.use(express.static('public')); } }

此时位于public目录中的文件可以直接访问:

http://localhost:3000/images/kitten.jpg http://localhost:3000/css/style.css http://localhost:3000/js/app.js http://localhost:3000/images/bg.png http://localhost:3000/hello.html

:::caution 注意 Express 是相对于静态目录查找文件的,因此静态目录的名称(public)不会出现在 URL 路径中——URL 直接以目录内的相对路径访问。 :::

如果想调整路由前缀,可以通过下面的方式指定挂载路径:

app.use('/static', express.static(path.join(__dirname, 'public')));

这样public/hello.html的访问地址就变成了http://localhost:3000/static/hello.html。express.static还支持第二个参数传入配置对象,可设置maxAge(缓存时长)、setHeaders(自定义响应头)、index、fallthrough等选项,详见 Express 官方文档的静态文件章节。

五、在 Serverless 场景使用:必须开启 buffer 返回

Serverless 场景较为特殊:网关不支持流式处理,因此不能使用默认的流式响应方式返回文件,需要选择支持 buffer(一次性读入内存并返回)的静态中间件。koa-static-cache恰好支持 buffer 返回。

首先安装依赖:

$ npm i koa-static-cache --save

然后在src/configuration.ts中注册中间件,注意将buffer显式设为true:

// src/configuration.ts import { Configuration, App } from '@midwayjs/decorator'; import { Application } from '@midwayjs/faas'; import * as staticCache from 'koa-static-cache'; @Configuration() export class AutoConfiguration { @App() app: Application; async onReady() { this.app.use( staticCache({ prefix: '/public/', dir: join(__dirname, '../public'), dynamic: true, preload: false, buffer: true, // 注意,这里是 true maxFiles: 1000, }) ); } }

各选项含义:

  • buffer: true:将文件内容读入内存一次性返回,规避网关不支持流式响应的问题;
  • dynamic: true:动态加载文件,不在初始化时一次性扫描目录;
  • preload: false:配合dynamic使用,不在启动时预加载全部文件;
  • maxFiles: 1000:动态缓存最多缓存的条目数,超出后按 LRU 策略淘汰。

在非高密度场景(普通函数)下,还需要提供一个/*的路由函数,否则请求根本不会进入函数逻辑,自然也就走不到中间件中。为了保证中间件可进入,可以增加一个空的Get /public/*路由——写成public/*是为了防止其他非 public 静态资源的请求误入这个函数:

import { Inject, Provide, Controller, Get } from '@midwayjs/decorator'; import { Context } from '@midwayjs/faas'; @Provide() export class ServerlessHelloService { @Inject() ctx: Context; // 普通路由 @ServerlessTrigger(ServerlessTriggerType.HTTP, { path: '/:user_id', method: 'get', }) async hello1() { return 22; } @ServerlessTrigger(ServerlessTriggerType.HTTP, { path: '/public/*', method: 'get', }) async render() { // 这个函数的作用是为了让 static 全局中间件被执行。 } }

说明:本文示例引用自 Midway 2.x 文档(site/versioned_docs/version-2.0.0/static_file.md),其中使用的@ServerlessTrigger等装饰器为 2.x 时代 API。当前仓库的@midwayjs/faas已演进为@midwayjs/hooks等新一代函数式 API,但「Serverless 网关不支持流式、需用 buffer 返回 + 兜底路由触发中间件」的核心原理与约束仍然成立。

六、组件化方案:@midwayjs/static-file 统一托管

除了在各运行时手工挂载中间件,Midway 还提供了开箱即用的静态文件组件@midwayjs/static-file,源码位于仓库 packages/static-file。它基于koa-static-cache封装,同时适用于 koa / egg / faas,并把默认配置、多目录支持、缓存策略和 Range 请求支持都收敛到组件内部。

6.1 安装与引入

$ npm i @midwayjs/static-file --save

在configuration.ts中引入组件:

import * as koa from '@midwayjs/koa'; import * as staticFile from '@midwayjs/static-file'; import { join } from 'path'; @Configuration({ imports: [ koa, staticFile, ], importConfigs: [ join(__dirname, './config') ] }) export class ContainerConfiguration { }

组件注册后,$appDir/public目录下的所有静态文件都可以通过/public前缀访问,且默认懒加载(dynamic 模式,首次访问才读盘)。

6.2 默认配置解析(源码级)

组件的默认配置定义在 src/config/config.default.ts:

export default appInfo => { return { staticFile: { dirs: { default: { prefix: '/public', dir: join(appInfo.appDir, 'public'), }, }, dynamic: true, preload: false, buffer: false, maxFiles: 1000, }, } as { staticFile: StaticFileOptions; }; };

生产环境配置定义在 src/config/config.prod.ts:

export const staticFile = { maxAge: 31536000, buffer: true, } as StaticFileOptions;

两套配置合起来形成了组件完整的默认行为:

配置项开发环境默认值生产环境默认值说明
dirs.default.prefix/public/publicURL 路径前缀
dirs.default.dir$appDir/public$appDir/public静态文件磁盘目录
dynamictruetrue动态加载,不在初始化时扫描全目录
preloadfalsefalse不预加载文件缓存
bufferfalsetrue生产环境改为内存 buffer 返回
maxFiles10001000动态缓存条目上限(LRU)
maxAge031536000缓存控制 max-age(生产一年)

由此可以得出两组实用结论:

  • 开发环境:文件不做缓存、走磁盘流式读取,修改静态资源后刷新浏览器即可立即生效;
  • 生产环境:文件在首次访问后被缓存(buffer 模式读入内存),更新资源后需要重启进程才能生效;同时maxAge被设置为 31536000(一年),利于浏览器长期缓存。

6.3 多目录支持:dirs 配置

组件基于 src/interface.ts 中的StaticFileOptions,额外提供了dirs字段,允许同时托管多个静态目录,每个目录可以有自己的前缀与独立配置:

// {app_root}/src/config/config.default.ts export const staticFile = { dirs: { default: { prefix: '/public', dir: 'xxx' }, anotherDir: { prefix: '/', dir: 'xxx' } } };

可以看到中间件 src/middleware/static.middleware.ts 的实现:启动时会遍历staticFileConfig.dirs的所有值(若配置了顶层dir也会追加进去),对每个目录分别执行staticCache(newOptions)生成一个静态服务,最后用middlewareService.compose把多个中间件组合起来。也就是说,一个应用可以同时以不同前缀服务多套资源目录,例如/public服务前端构建产物、/服务 favicon 等根路径资源。

若只想覆盖默认目录的前缀(例如把/public改为/),直接覆盖dirs.default即可:

// {app_root}/src/config/config.default.ts export const staticFile = { dirs: { default: { prefix: '/', }, } };

6.4 中间件底层原理

中间件核心逻辑位于 src/middleware/static.middleware.ts,关键点如下:

  1. Range 请求支持:组件额外集成了koa-range(依赖声明见 packages/static-file/package.json),当请求路径命中任一静态前缀时,会先经过 range 中间件,使大文件(如视频、PDF)支持 HTTP 断点续传与分段下载。
  2. LRU 动态缓存:当dynamic为true且未显式传入files时,会自动创建一个容量为maxFiles的 LRU 缓存(基于ylru实现),避免无上限缓存撑爆内存。
  3. 目录存在性校验:启动时会检查dir是否存在,不存在则抛出组件定义的DirectoryNotFoundError(错误码static_file/10000,定义在 src/error.ts),并打印[midway:static] starting static serve <prefix> -> <dir>日志便于排查。
  4. 环境感知的 faas 适配:配置类 src/configuration.ts 中,onConfigLoad检测到当前应用为faas时,会自动把buffer置为true(呼应本文第五节「Serverless 必须 buffer 返回」的结论);onReady则把StaticMiddleware注册到 koa / faas / egg 三类应用上——若同时启用了cross-domain组件,会插入到 cors 中间件之后,否则插入到最前。

6.5 完整配置项一览

组件透传koa-static-cache的全部配置(接口定义见 packages/static-file/src/interface.ts),常用项说明:

配置项类型说明
prefixstringURL 路径前缀
dirstring要托管的磁盘目录
dirsobject多目录配置,key 为目录别名,value 为目录级配置对象
dynamicboolean是否动态加载文件(不在初始化时全量缓存)
preloadboolean是否在初始化时预加载全部文件,通常与dynamic搭配使用
bufferboolean是否将文件读入内存返回(Serverless 场景必须为true)
maxFilesnumber动态缓存的最大条目数,仅dynamic: true时生效,默认1000
maxAgenumber缓存控制 max-age(秒),默认0,生产环境31536000
cacheControlstring自定义 Cache-Control 响应头,优先级高于maxAge
gzipboolean请求 Accept-Encoding 含 gzip 时,对文件做 gzip 压缩
aliasobject路径别名映射,可用不同 URL 访问同一文件
filterfunction \| string[]初始化扫描目录时过滤文件,可排除源文件等非构建产物;传数组则只允许列出的文件

七、场景选型速查与注意事项

运行时推荐方案关键点
@midwayjs/web(Egg)egg-static插件src/config/plugin.ts中exports.static = true,默认prefix: '/public/'、dir: $baseDir/app/public
@midwayjs/koakoa-static-cacheonReady中app.use(staticCache({ prefix, dir }))
@midwayjs/express内置express.static目录名不进入 URL;可app.use('/static', express.static(...))指定前缀
@midwayjs/faas(Serverless)koa-static-cache或@midwayjs/static-filebuffer: true;普通函数需提供/public/*兜底路由以触发中间件

实践中的常见注意点:

  1. 路径前缀与目录名:Egg / Koa 方案中prefix会体现在 URL 上(如/public/a.js),Express 方案中静态目录名默认不体现,需按需挂载前缀,避免前端资源引用路径 404。
  2. 缓存一致性:使用@midwayjs/static-file时,生产环境资源访问后被缓存,更新文件必须重启进程;开发环境则实时生效,两者行为差异来自config.prod.ts的buffer: true与maxAge配置。
  3. Serverless 的双重约束:既要开启buffer: true适配网关,又要准备兜底路由让静态请求能进入函数与中间件链路,二者缺一不可。
  4. 目录校验:组件启动时会强制校验dir存在,目录路径拼错会直接抛DirectoryNotFoundError,日志中的[midway:static] starting static serve是定位问题的第一线索。
  5. 大文件与 Range:@midwayjs/static-file内置koa-range支持分段请求,适合音视频等大文件场景;自行使用koa-static-cache时如需 Range 能力需额外引入。

综上,Midway 的静态资源托管既可以通过各运行时原生的中间件快速接入(Egg / Koa / Express / Serverless 四种写法),也可以直接使用@midwayjs/static-file组件获得多目录、LRU 缓存、Range 与 faas 自动 buffer 等能力。选择哪一种,取决于你的运行时类型、是否需要组件化统一管理,以及对缓存与 Serverless 网关约束的具体要求。

  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载
上一篇:Feeder:重新定义RSS阅读体验的开源解决方案
下一篇:3个创新方案:在华为HarmonyOS设备上完美部署MicroG服务

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询