- 后端
- 微服务
- 云原生
【免费下载链接】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. 🌈
导读
本文以 Midway 仓库中 packages/static-file/CHANGELOG.md 的版本演进为主线,结合 packages/static-file/README.md 与组件源码,系统讲解@midwayjs/static-file的安装引入、默认行为、全部配置项、多目录静态服务以及中间件底层实现。读完本文,你将能够在 koa / egg / faas 三种应用中快速搭建静态资源服务,理解懒加载、内存缓存与生产/非生产环境差异,并掌握如何通过源码与测试验证组件行为。
组件定位:Midway 的静态资源服务方案
@midwayjs/static-file是 Midway 官方提供的静态资源托管组件,其定位在 packages/static-file/README.md 中描述得很清楚:基于 koa 生态的 koa-static-cache 实现,可同时用于koa / egg / faas三种应用形态。
从 CHANGELOG.md 可以看到该组件的引入节点:v3.0.0-beta.17(2022-01-18)的 Feature 条目 "add static file" 正是它的诞生记录,随后在 v3 系列中持续迭代修复。当前仓库中组件版本号已演进到4.2.3(见 packages/static-file/package.json),要求 Node.js>=20。
在 packages/static-file/src/index.ts 中,组件对外统一导出Configuration(即 configuration.ts)、中间件、全部配置接口类型以及错误类型,是标准的 Midway 组件接入形态。
安装与引入
安装命令(README 原文):
$ npm i @midwayjs/static-file --save引入方式是在应用的configuration.ts中通过imports声明组件,示例代码如下:
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 { }组件内部的挂载逻辑见 packages/static-file/src/configuration.ts:
- 在
onReady阶段,通过MidwayApplicationManager.getApplications(['koa', 'faas', 'egg'])获取三类应用实例; - 若容器中已存在
cross-domain命名空间(即引入了跨域组件),静态中间件会插入到 CORS 中间件之后(insertAfter(StaticMiddleware, 'cors')),避免跨域头被静态响应覆盖;否则插入到最前(insertFirst),保证静态文件请求最先被处理; onConfigLoad中检测到 faas 环境时会额外强制buffer: true(详见后文 serverless 适配)。
默认配置与默认行为
组件的默认配置定义在 packages/static-file/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, }, }; };生产环境配置 packages/static-file/src/config/config.prod.ts 在此基础上覆盖两项:
export const staticFile = { maxAge: 31536000, buffer: true, };汇总为下表:
| 配置项 | 默认值(开发环境) | 生产环境(config.prod) | 说明 |
|---|---|---|---|
dirs.default.prefix | /public | /public | 静态资源 URL 前缀 |
dirs.default.dir | $appDir/public | $appDir/public | 静态资源目录 |
dynamic | true | true | 是否懒加载(动态发现文件) |
preload | false | false | 是否初始化时预加载全部资源 |
buffer | false | true | 是否将文件读入内存返回 |
maxFiles | 1000 | 1000 | 缓存条目上限 |
maxAge | 0 | 31536000(约 1 年) | 浏览器缓存有效期(秒) |
由此可以得出 README 中强调的核心行为:
$appDir/public下的所有静态文件,可通过/public前缀访问,且均为懒加载;- 非生产环境不缓存资源,修改文件后立即生效,方便开发调试;
- 生产环境访问过的资源会被缓存,更新资源后需要重启进程才能生效(
buffer: true+ 1 年maxAge的强缓存策略)。
配置项详解
组件完整支持koa-static-cache的全部配置,并在类型定义 packages/static-file/src/interface.ts 中显式声明了每个选项的含义:
| 配置项 | 类型 | 说明 |
|---|---|---|
prefix | string | URL 前缀 |
dir | string | 要托管的静态目录 |
dynamic | boolean | 是否在初始化后动态加载文件(不预缓存) |
preload | boolean | 初始化时是否预缓存全部资源,默认 true,通常与dynamic配合使用 |
buffer | boolean | 是否将文件存入内存返回,而不是每次请求都从文件系统流式读取 |
maxFiles | number | 缓存条目上限,仅在dynamic为 true 时生效,默认 1000 |
maxAge | number | 缓存控制最大有效期(秒),默认 0 |
cacheControl | string | 可选的 Cache-Control 响应头,优先级高于maxAge |
gzip | boolean | 当请求的 Accept-Encoding 包含 gzip 时,对文件进行 gzip 压缩 |
alias | object | 路径别名映射 |
filter | function \| string[] | 初始化扫描目录时过滤文件,例如跳过非构建产物;传数组则仅允许列出的文件 |
其中maxFiles是组件额外提供的选项(README 原文),用于控制动态缓存规模。
多目录静态服务配置
默认只托管$appDir/public一个目录,但组件支持通过dirs配置同时托管多个目录,每个目录可独立指定前缀:
// {app_root}/src/config/config.default.ts export const staticFile = { dirs: { default: { prefix: '/public', dir: 'xxx' }, antoherDir: { prefix: '/', dir: 'xxx' } } };如果只想覆盖默认前缀(例如让/public目录直接以根路径/访问),只需改写dirs.default:
// {app_root}/src/config/config.default.ts export const staticFile = { dirs: { default: { prefix: '/', }, } };测试夹具 packages/static-file/test/fixtures/koa-with-different-dirs/src/config.default.ts 展示了双目录、双前缀的完整写法——default目录绑定/、another目录绑定/static:
export const staticFile = { dirs: { default: { prefix: '/', dir: join(__dirname, '../public') }, another: { prefix: '/static', dir: join(__dirname, '../static') } } };注意:dirs中多个目录也可以共享同一个前缀(见 koa-with-dirs 的夹具,两个目录均绑定/),静态中间件会依次尝试匹配,因此同名文件在靠前的目录命中后即返回。
中间件实现原理
组件核心是 packages/static-file/src/middleware/static.middleware.ts 中的StaticMiddleware,其resolve方法揭示了完整的处理链路:
- 汇总目录:读取
staticFileConfig.dirs的每个值,若还存在顶层dir配置也一并加入,统一遍历处理; - Range 支持:注册一个前置
rangeMiddleware,一旦请求路径命中任一已注册前缀,就交由koa-range(package.json 中的依赖koa-range@0.3.0)处理 Range 请求,支持视频/大文件的断点续传; - 逐目录创建缓存器:将全局配置与单个目录配置合并(
Object.assign({}, this.staticFileConfig, dirObj),目录级配置优先);当dynamic为 true 且未提供files时,使用ylru库构造LRU(maxFiles)作为动态缓存; - 目录存在性校验:pkg 打包环境用
fs.existsSync同步校验,源码运行环境用异步FileUtils.exists校验;目录不存在则抛出DirectoryNotFoundError(定义于 packages/static-file/src/error.ts,错误码static_file/10000,消息为Path xxx not exist, please check it.); - 组合执行:将
rangeMiddleware与每个目录的staticCache(newOptions)依次compose成一个中间件串返回。
StaticMiddleware.getName()返回'staticFile',与 configuration.ts 中的命名空间static-file对应,也便于日志中标识(启动时会打印[midway:static] starting static serve <prefix> -> <dir>)。
测试与验证
组件的行为在 packages/static-file/test/index.test.ts 中有三个典型用例可直接验证:
- 同前缀多目录:
koa-with-dirs夹具下,/foo.js与/index.html都能正确返回文件内容(console、<body>hello</body>); - 不同前缀多目录 + Range:
koa-with-different-dirs夹具下,/foo.js走根前缀、/static/index.html走/static前缀;且对/foo.js发起Range: bytes=0-10请求时,响应携带content-length: 11、Accept-Ranges: bytes、Content-Range: bytes 0-10/20,验证了断点续传能力; - faas 环境:
faas-with-dirs夹具通过createLegacyFunctionApp启动,/foo.js同样正常返回。
运行方式(package.json):
$ npm run test # 或 npm run ci / npm run cov版本演进与关键修复
CHANGELOG 中除大量 "Version bump only"(lerna 发布时生成的版本号占位记录,不包含实际代码变更)外,可以梳理出如下实质性演进:
| 版本 | 类型 | 变更内容 |
|---|---|---|
| 3.0.0-beta.17 | Feature | 新增 static-file 组件,首次落地 |
| 3.0.0-beta.3 | Feature | 增加组件与框架配置定义(StaticFileOptions类型体系) |
| 3.0.1 | Bug Fix | 补充缺失的maxAge配置,修复静态响应缺失缓存头的问题 |
| 3.0.2 | Bug Fix | 修复 singleton 调用下 request scope 不生效的问题 |
| 3.0.4 | Bug Fix | 修复 supertest 类型与createFunctionApp相关问题 |
| 3.1.0 | Bug Fix | 使用 hook 加载 egg 应用,适配 egg 场景 |
| 3.2.1 | Bug Fix | 修复 swagger UI 替换 json path(静态资源与 swagger 路径协同) |
| 3.4.0-beta.4 | Bug Fix | serverless 环境下返回 buffer——与 configuration.ts 中 faas 环境强制buffer: true的实现对应 |
| 3.6.0 | Feature | 增加 guard 支持,使静态中间件可被 guard 体系保护 |
其中 3.4.0-beta.4 的 "return buffer in serverless environment" 尤其值得关注:在函数计算场景下,流式读取文件系统可能受限,组件因此在检测到 faas 应用时自动开启内存缓冲模式,这正是 configuration.ts 中onConfigLoad钩子的实现动机,也解释了为什么生产环境默认buffer: true。
使用建议小结
- 开发环境:保持默认即可,
dynamic: true+buffer: false保证改文件即时生效; - 生产环境:默认 1 年
maxAge与buffer: true适合内容基本不变的打包产物,更新资源后需重启进程;若资源频繁变动,可自行覆盖maxAge或关闭buffer; - 多目录场景:用
dirs为不同目录(如用户上传目录、前端构建产物目录)分配不同前缀,并注意目录必须真实存在,否则启动时会抛出DirectoryNotFoundError; - 大文件/音视频:组件内置 Range 支持,可放心用于断点续传场景,相关断言可在测试用例中复现验证。
- 后端
- 微服务
- 云原生
【免费下载链接】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. 🌈
相关推荐
Midway 静态资源组件实战指南:基于 @midwayjs/static-file 的静态文件服务与缓存配置
Midway 静态资源组件实战指南:基于 @midwayjs/static file 的静态文件服务与缓存配置 @midwayjs/static file 是
后端微服务云原生Agregarr性能优化:让你的Plex收藏更新速度提升50%
Agregarr性能优化:让你的Plex收藏更新速度提升50% 想要让你的Plex收藏管理体验更加流畅高效吗?😊 Agregarr作为一款强大的Plex收藏管
后端微服务云原生Cloudflare Workers 静态资源(Static Assets)部署与配置完全指南
Cloudflare Workers 静态资源(Static Assets)部署与配置完全指南 Cloudflare Workers Static Assets
人工智能AI 技能AI 插件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考