首页/新闻资讯/正文详情

@eggjs/koa-static-cache:面向 Koa 的静态缓存中间件全解析

发布时间:2026/9/21 1:44:41 来源:云帆数科 栏目:资讯中心
@eggjs/koa-static-cache:面向 Koa 的静态缓存中间件全解析
eggjs/koa-static-cache面向 Koa 的静态缓存中间件全解析【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址: https://gitcode.com/gh_mirrors/eg/eggeggjs/koa-static-cache是 Egg 框架团队从 koajs/static-cache fork 而来、并以 TypeScript 重写的 Koa 静态资源缓存中间件同时支持 CommonJS 与 ESM。它以启动时缓存 内存缓冲 按需 gzip MD5 ETag为设计主线为静态资源服务提供了比传统流式静态中间件更高的缓存命中率与更低的重复 I/O。读完本文你将掌握它的全部配置项语义、底层请求处理链路以及如何将其作为eggjs/static插件的底座在 Egg 应用中使用。与同类静态中间件的核心差异中间件的官方文档开篇即声明了它与 koajs/static 等库的本质区别理解这五点有助于你决定何时选用它不支持目录列表与index.html自动回退——它只服务具体存在的文件可选将文件内容驻留内存buffer而非每次请求都从磁盘流式读取默认在初始化阶段preload就把目录资产扫描进缓存因此正常情况下需要重启进程才能感知资产更新可通过options.preload false关闭使用 MD5 哈希摘要作为 ETag同时输出Content-MD5响应头便于强缓存与内容校验优先使用磁盘上已存在的.gz预压缩文件行为类似 nginx 的gzip_static模块避免运行时重复压缩开销。安装与基本使用包名发布在eggjs作用域下npm install eggjs/koa-static-cache根据 package.json当前版本引擎要求node 22.18.0包以 ESM 为默认模块类型type: module同时通过exports与构建产物兼容两类模块系统。最小可用示例CommonJSconst path require(path); const { staticCache } require(eggjs/koa-static-cache); app.use( staticCache(path.join(__dirname, public), { maxAge: 365 * 24 * 60 * 60, }), );staticCache在 src/index.ts 中提供了一组函数重载支持四种调用形态最终全部收敛为staticCache(dir, options, files)staticCache()——目录默认取process.cwd()staticCache(dir)staticCache(options)——目录取自options.dirstaticCache(dir, options)staticCache(dir, options, files)——第三参数是外部文件存储见下文 Files 章节。需要特别说明的参数优先级第一个字符串参数dir的优先级高于options.dir这由源码中if (!dir options.dir) dir options.dir;的逻辑决定对应测试用例 should dir priority than options.dir 也验证了这一点见 test/index.test.ts。目录最终会经过path.normalize归一化。API 选项逐一详解以下选项与官方文档一一对应并补充源码层面的默认值与行为细节类型定义见 src/index.ts。选项类型默认值说明dirstringprocess.cwd()静态资源根目录maxAgenumber0Cache-Control的max-age秒数cacheControlstring \| (path) stringundefined自定义缓存控制头优先级高于maxAgebufferbooleanfalse是否将文件内容读入内存替代每次请求流式读取gzipbooleanfalse客户端accept-encoding含 gzip 时运行时压缩响应usePrecompiledGzipbooleanfalse优先使用磁盘上的.gz预压缩文件类似 nginxgzip_staticaliasobject{}URL 别名映射见 Aliases 章节prefixstringURL 前缀见下文dynamicbooleanfalse是否允许动态加载初始化时未缓存的文件filterfunction \| string[]undefined初始化扫描目录时的文件过滤数组形式则仅白名单这些文件preloadbooleantrue是否在初始化时扫描并缓存全部资产常与dynamic配合使用filesobjectundefined外部文件存储普通对象或 LRU 实例见 Files 章节dir服务根目录默认process.cwd()。实践中建议始终显式传入绝对路径避免进程启动目录不确定导致服务错乱。maxAge 与 cacheControl缓存控制头maxAge默认0最终以public, max-agen的形式写入Cache-Control响应头。而cacheControl支持两种形态字符串直接作为Cache-Control的值覆盖maxAge函数接收文件绝对路径为参数返回字符串实现按文件差异化缓存策略。源码在loadFile中执行typeof options.cacheControl function ? options.cacheControl(filename) : options.cacheControl见 src/index.ts。测试用例 should support cacheControl function见 test/index.test.ts展示了函数形态的用法对index.ts返回public, max-age1000对其他文件返回public, max-age0两个请求分别得到对应的Cache-Control头。buffer内存缓冲模式默认false时中间件按需以createReadStream流式发送文件内存占用低但每次请求都有磁盘 I/O设为true后文件内容在loadFile阶段通过readFileSync一次性读入内存obj.buffer buffer后续请求直接发送 Buffer适合体积小、访问量高的场景。gzip 与 usePrecompiledGzip压缩策略双通道两者语义不同gzip: true运行时压缩。源码中只有当file.length 1024超过 1KB且文件 MIME 类型可压缩通过eggjs/compressible判断、且客户端声明支持 gzip 时才会压缩压缩结果缓存到zipBuffer后复用避免重复压缩usePrecompiledGzip: true优先使用磁盘上的.gz文件。当请求命中 gzip 时若缓存中存在filename.gz的预压缩内容gzFile gzFile.buffer直接取用而不重新压缩——这正是 nginxgzip_static的思路。无论哪种压缩中间件都会在启用 gzip 时设置Vary: Accept-Encodingctx.vary(Accept-Encoding)保证缓存代理不会把 gzip 响应误发给不支持 gzip 的客户端。测试用例 should serve files with gzip buffer 验证了 gzip 响应同时携带Content-Encoding: gzip、Vary: Accept-Encoding与Content-Length头见 test/index.test.ts。prefixURL 前缀prefix默认空字符串。源码会对它做归一化处理(options.prefix ?? ).replace(/\/*$/, /)保证前缀以单个/结尾并取filePrefix path.normalize(options.prefix.replace(/^\//, ))用于动态加载时裁剪路径。请求处理首先校验ctx.path.startsWith(options.prefix)不匹配则直接next()放行见 src/index.ts。测试用例 should serve files with prefix 验证了/static/src/index.ts形态的访问。filter初始化扫描过滤filter支持两种形态见 src/index.ts函数(filePath) boolean返回true的文件才会被预加载常用于跳过源码、构建中间产物等字符串数组等价于白名单仅当文件名存在于数组中才加载。测试用例分别验证了函数形态排除node_modules与数组形态filter: [index.js]时请求/README.md返回 404的行为见 test/index.test.ts。preload 与 dynamic初始化缓存与动态加载preload默认true中间件在创建时通过fs-readdir-recursive递归扫描dir自动跳过以.开头的隐藏文件与node_modules目录然后逐个调用loadFile将文件元数据含 MD5写入缓存。dynamic默认false开启后请求未命中缓存的文件时中间件会在运行时尝试加载拒绝隐藏文件path.basename(filename)[0] .裁剪prefix前缀得到相对路径拼接完整路径并做目录逃逸防护fullpath.startsWith(dir)不成立则直接放行对应测试 should loadFile under options.dir对/%2E%2E/package.json的路径穿越请求返回 404校验文件存在且为普通文件后调用loadFile写入缓存。两个选项的配合关系是关闭 preload、开启 dynamic 即懒加载模式——启动时不扫描目录首次请求才加载并缓存而preload: true, dynamic: false则是启动全量缓存 拒绝新文件的经典生产模式。测试 should options.dynamic and options.preload works fine 验证了preload: false, dynamic: true时初始files为空对象、请求后缓存被填充见 test/index.test.ts。files外部文件存储files可以传入普通对象也可以是实现了get(key)/set(key, value)两个方法的存储实例如lru-cache或ylru。FileManager类在构造时会通过typeof store.set function typeof store.get function区分两类存储并统一封装见 src/index.ts。这一设计带来三个实用能力1. 将多个目录合并进单个中间件与其挂载两次中间件app.use(staticCache(/public/js)); app.use(staticCache(/public/css));不如共享同一个files对象让两个目录的缓存落在同一份映射里减少一次函数栈调用与一次哈希查找const files {}; // 挂载中间件 app.use(staticCache(/public/js, {}, files)); // 追加目录到同一存储 staticCache(/public/css, {}, files);2. 运行时编辑缓存元数据由于files暴露了每个路径的元数据对象你可以事后修改单个文件的缓存策略。例如把/package.json的maxAge从一年改成一个月const files {}; app.use( staticCache( /public, { maxAge: 60 * 60 * 24 * 365, }, files, ), ); files[/package.json].maxAge 60 * 60 * 24 * 30;测试 should be configurable via object 正是通过修改files[/package.json].maxAge 1并断言响应头变为Cache-Control: public, max-age1来验证该能力见 test/index.test.ts。3. 使用 LRU 缓存避免动态模式下的 OOM动态模式下缓存会无限增长官方文档建议注入带容量上限的 LRU 实例const LRU require(lru-cache); const files new LRU({ max: 1000 }); app.use( staticCache({ dir: /public, dynamic: true, files, }), );测试 should work fine when new file added in dynamic mode with LRU见 test/index.test.ts使用容量为 1 的ylru实例验证了 LRU 淘汰行为连续请求a.js、b.js、c.js后a.js、b.js依次被挤出缓存再次访问a.js又挤掉了c.js。aliasURL 别名alias是 URL 路径到真实文件路径的映射不产生重定向内部直接改写查找键。典型场景是 favicon站内多处引用/favicon.png磁盘上只保留一张favicon-32.pngconst options { alias: { /favicon.png: /favicon-32.png, }, };请求/favicon.png时实际返回/favicon-32.png的内容。源码在路径解码与归一化之后执行别名替换if (options.alias options.alias[filename]) filename options.alias[filename]见 src/index.ts。测试用例同时覆盖了 POSIX 与 Windows 路径分隔符两种别名键见 test/index.test.ts。请求处理链路与缓存原理解析将 src/index.ts 中的中间件主流程展开一次请求的完整路径如下方法过滤仅处理GET与HEAD其余方法直接next()放行测试 should 404 Not Found for other Methods 表明PUT请求会落到下游路由返回 404前缀校验ctx.path不以prefix开头则放行路径归一化先safeDecodeURIComponent解码支持/%E4%B8%AD%E6%96%87这类编码路径再做path.normalize容忍//index这类异常路径对应测试 should accept abnormal path别名替换命中alias则改写文件名查缓存files.get(filename)命中则直接使用未命中时按上文 dynamic 章节的流程决定是否动态加载否则放行新鲜度校验非 buffer 模式下先用fs.stat对比mtime若磁盘文件时间戳变化则失效 MD5 并刷新长度随后设置Last-Modified与ETag若ctx.fresh为真则返回304 Not Modified对应测试 should support conditional HEAD/GET requests响应组装设置Content-Type、Content-Length有 gzip 时用zipBuffer.length、Cache-Control、Content-MD5HEAD请求在此结束内容发送按 buffer / 预压缩 gzip / 流式 / 运行时 gzip 四种分支发送响应体。ETag 与 Content-MD5MD5 双头校验loadFile在加载文件时即计算obj.md5 crypto.createHash(md5).update(buffer).digest(base64)见 src/index.ts它同时被用作ETag响应头ctx.response.etag file.md5Content-MD5响应头ctx.set(content-md5, file.md5)。测试 should set the etag and content-md5 headers 用同一 MD5 算法独立计算package.json的摘要断言响应头ETag为base64 md5且Content-MD5与之相等见 test/index.test.ts。流式模式下文件未被整体读入内存MD5 无法在加载时计算中间件会延迟到首次流式发送时通过stream.on(data/end)增量计算并缓存之后即可提供 ETag 支持见 src/index.ts。304 条件请求中间件完整支持基于Last-Modified与ETag的条件请求客户端携带If-None-Match或If-Modified-Since访问时命中缓存校验后由ctx.fresh判定直接返回304不发送正文。测试覆盖了 GET 与 HEAD 两种方法的条件请求见 test/index.test.ts。在 Egg 应用中的集成eggjs/static 插件eggjs/koa-static-cache是 Egg 内置静态服务插件eggjs/static的底层实现。在 packages/egg/src/config/plugin.ts 中static插件默认启用其配置类型声明位于 plugins/static/src/types.ts实际逻辑见 plugins/static/src/app/middleware/static.ts。eggjs/static透传koa-static-cache的全部选项并在此基础上定义了自己的默认值见 plugins/static/src/config/config.default.ts 与 plugins/static/README.mdprefix: /public/dir: path.join(appInfo.baseDir, app/public)dynamic: true懒加载preload: falsemaxAge生产环境31536000其他环境0buffer生产环境true其他环境false额外选项maxFiles: 1000动态模式下缓存条目上限插件内部在dynamic开启且未传入files时自动注入new LRU(newOptions.maxFiles)见 plugins/static/src/app/middleware/static.ts。由此带来两个对开发体验影响深远的行为非生产环境资源不做缓存修改即生效方便开发调试生产环境资源被访问后才缓存更新资产需要重启进程——这正是底层preload缓存模型在 Egg 侧的体现。dir还支持多目录形式dir: [dir1, dir2, ...]或dir: [dir1, { prefix: /static2, dir: dir2 }]插件会为每个目录分别实例化一个staticCache中间件并用koa-compose组合同时通过koa-range提供 Range 分片支持见 plugins/static/src/app/middleware/static.ts。在 Egg 中自定义静态资源配置// {app_root}/config/config.default.ts export default { static: { // 覆盖缓存时长maxAge: 31536000, }, };实战建议与注意事项综合文档、源码与测试给出几条可落地的使用建议生产环境优先buffer: true将高频小文件驻留内存配合 ETag/304 大幅降低带宽与磁盘 I/O超大文件建议保持流式buffer: false避免撑爆内存。善用cacheControl函数按文件差异化缓存例如index.html不缓存、带哈希指纹的静态资源长缓存这是maxAge全局配置无法做到的。开发期使用preload: false, dynamic: true文件即时生效生产期配合buffer: true获得最佳性能代价是更新需重启进程——发布前可借助带内容哈希的文件名规避。多目录合并时共享files对象既减少中间件栈深度又能通过编辑元数据实现细粒度缓存策略。动态模式务必配置容量受限的 LRU无上限的缓存增长会带来 OOM 风险这正是官方文档专门给出 LRU 示例的原因。路径安全中间件内置了前缀裁剪与fullpath.startsWith(dir)双重防线但自定义prefix与alias时应保持路径规范避免意外暴露目录外文件。总结eggjs/koa-static-cache通过启动预加载 内存缓冲 增量 MD5 ETag 双通道 gzip的组合把静态资源服务从每次请求读磁盘优化为初始化一次、按需零拷贝复用同时通过files外部存储、filter、cacheControl等设计保持了极高的灵活性。无论是作为 Koa 应用的独立中间件直接使用还是作为 Egg 内置eggjs/static插件的底座它都是一份值得研读的静态缓存实现范本——其完整的参数语义、条件请求与路径安全实现均可在 源码 与 测试 中得到印证。【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址: https://gitcode.com/gh_mirrors/eg/egg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

通达信“飞龙在天”主图指标:均线交易系统与买卖信号源码详解
通达信“飞龙在天”主图指标:均线交易系统与买卖信号源码详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 1:44:41

Vitess v17.0.6 破坏性变更解析:ExecuteFetchAsDBA 拒绝多语句 SQL 的来龙去脉
Vitess v17.0.6 破坏性变更解析:ExecuteFetchAsDBA 拒绝多语句 SQL 的来龙去脉

数据库分布式数据库云原生后端数据存储 【免费下载链接】vitess Vitess is a database clustering system for horizontal scaling of MySQL. 项目地址: https://gitcode.com/gh_mirrors/vi/vitess 点击查看 免费下载 本指南深入剖析 Vitess v17.0.6 引入的一项破坏… · 2026/9/21 1:44:41

Grafana图像渲染器依赖问题深度解析与生产级部署指南
Grafana图像渲染器依赖问题深度解析与生产级部署指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 1:44:41

WTF终端仪表盘10个必装模块精选:从GitHub到天气,打造你的终极信息中枢
WTF终端仪表盘10个必装模块精选:从GitHub到天气,打造你的终极信息中枢

WTF终端仪表盘10个必装模块精选:从GitHub到天气,打造你的终极信息中枢 【免费下载链接】wtf The personal information dashboard for your terminal 项目地址: https://gitcode.com/gh_mirrors/wt/wtf WTF(wtfutil)是一款免费开源的终端个人仪表盘,专为开发者和技术爱好… · 2026/9/21 2:28:48

CodeIgniter XML Helper 完全指南:xml_convert 用法、原理与实战
CodeIgniter XML Helper 完全指南:xml_convert 用法、原理与实战

后端Web框架 【免费下载链接】CodeIgniter Open Source PHP Framework (originally from EllisLab) 项目地址: https://gitcode.com/gh_mirrors/co/CodeIgniter 点击查看 免费下载 XML Helper 是 CodeIgniter 框架中用于辅助处理 XML 数据的函数集合,其… · 2026/9/21 2:28:48

用户画像7大维度实战:从数据清洗到标签落地
用户画像7大维度实战:从数据清洗到标签落地

用户画像这几年已经被说烂了,但真正能把画像做扎实、能直接支撑业务决策的大数据分析师,其实并不多。尤其是旅游网站这类垂直领域,用户的决策链路长、场景碎片化,画像要是只停留在“性别年龄城市”的粗粒度标签,那基本… · 2026/9/21 2:28:48

飞书与腾讯会议API对接实战:SSO、鉴权与事件回调全解析
飞书与腾讯会议API对接实战:SSO、鉴权与事件回调全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 2:28:48

Zephyr 板级支持详解:Renesas EK-RX261 评估套件的硬件特性、烧录与调试指南
Zephyr 板级支持详解:Renesas EK-RX261 评估套件的硬件特性、烧录与调试指南

Zephyr 板级支持详解:Renesas EK-RX261 评估套件的硬件特性、烧录与调试指南 【免费下载链接】zephyr Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures. 项目… · 2026/9/21 2:28:48

16页月度薪酬分析报告:从数据到决策的完整方法论
16页月度薪酬分析报告:从数据到决策的完整方法论

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 2:27:48

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化
Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡… · 2026/9/21 0:02:39

Word表格编号全攻略:从列表编号到题注交叉引用
Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技… · 2026/9/21 0:02:39

从第一个站到第二个站:独立开发者的静态网站选型与落地实践
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&… · 2026/9/20 0:00:41

Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行
Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 0:00:18

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程
agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and … · 2026/9/21 0:00:18

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,… · 2026/9/21 0:00:18

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码