如何解决Service Worker缓存.tyc文件时响应体为空的问题?
Service Worker缓存.tyc文件时仅存响应头、响应体为空的问题
我有一个基于PWA的站点,使用Service Worker实现离线功能,核心逻辑是监听页面fetch事件完成资源缓存、读取并响应请求。修改代码特殊处理.tyc后缀文件后出现缓存异常:.tyc文件仅缓存响应头,响应体为空,返回无效响应。
我的Service Worker代码
self.addEventListener('fetch', function(event){ event.respondWith(async function(){ if(event.request.url.endsWith(".tyc")){ var cacheMatch = await caches.match(event.request); if(cacheMatch){ debugger; cacheMatch.arrayBuffer().then((arrBuf)=>{ console.debug("got from cache:", arrBuf); }); return cacheMatch; } var res = await fetch(event.request); console.debug("fetched:", res); var cache = await caches.open('cache'); console.debug("got cache:", cache); console.debug("cached:", cache.put(event.request.url, res.clone())); return res; }else{ try{ var res = await fetch(event.request); var cache = await caches.open('cache'); cache.put(event.request.url, res.clone()); return res; }catch{ return caches.match(event.request); } } }()); });
预期逻辑
- .tyc文件请求:优先读缓存,无缓存则请求资源并存入缓存后返回(因文件体积大,优先用缓存)
- 其他请求:优先请求资源并存入缓存,请求失败则返回缓存资源
实际问题
- 非.tyc文件缓存逻辑正常
- .tyc文件流程正常,但缓存仅存响应头,响应体为空。删除缓存重新加载问题依旧,Chrome DevTools中.tyc文件显示“Nothing to preview”,执行
await (await fetch('https://example.com/default_3.7_7ea402.tyc')).arrayBuffer()返回ArrayBuffer(0),日志显示got from cache: ArrayBuffer(0)。
问题原因
当缓存命中.tyc文件时,调用cacheMatch.arrayBuffer()打印日志的操作会消费Response对象的响应体——Response的响应体是一次性可读流,一旦被读取(比如调用arrayBuffer()、text()等方法),流就会被耗尽,此时再返回原cacheMatch对象,它的响应体已经是空的,前端自然拿到无效响应。
另外,cache.put传入event.request.url作为缓存键虽可行,但更规范的是直接传入event.request对象,避免因请求参数(如headers)不同导致缓存不匹配。
修复后的代码
self.addEventListener('fetch', function(event){ event.respondWith(async function(){ if(event.request.url.endsWith(".tyc")){ var cacheMatch = await caches.match(event.request); if(cacheMatch){ debugger; // 克隆Response后读取响应体,避免原Response被消费 cacheMatch.clone().arrayBuffer().then((arrBuf)=>{ console.debug("got from cache:", arrBuf); }); return cacheMatch; } var res = await fetch(event.request); console.debug("fetched:", res); var cache = await caches.open('cache'); console.debug("got cache:", cache); // 使用event.request作为缓存键,提升匹配准确性 console.debug("cached:", cache.put(event.request, res.clone())); return res; }else{ try{ var res = await fetch(event.request); var cache = await caches.open('cache'); cache.put(event.request, res.clone()); return res; }catch{ return caches.match(event.request); } } }()); });
修复说明
- 缓存命中时,对
cacheMatch执行clone()后再读取响应体打印日志,保留原Response的响应体用于返回给页面。 - 将
cache.put的第一个参数从event.request.url改为event.request,确保缓存匹配的准确性,避免因请求属性差异导致缓存失效。
内容的提问来源于stack exchange,提问作者eac
相关产品推荐
相关产品推荐

