You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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);
      } 
    }
  }());
});

修复说明

  1. 缓存命中时,对cacheMatch执行clone()后再读取响应体打印日志,保留原Response的响应体用于返回给页面。
  2. 将cache.put的第一个参数从event.request.url改为event.request,确保缓存匹配的准确性,避免因请求属性差异导致缓存失效。

内容的提问来源于stack exchange,提问作者eac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:25:11