【问题标题】:fetch response stream manipulation获取响应流操作
【发布时间】:2018-06-03 07:41:06
【问题描述】:

在服务工作者中,我想将 HTML 注入 HTTP 响应正文:

当返回缓存的 HTML 响应时,我想在流式传输原始响应之前和之后的同时注释 <html> 元素(例如,将 <html lang="en"> 转换为 <html data-origin="cache" lang="en">)。

我已尝试基于Jake Archibald's streams fawning 实现此功能,但无法正常工作。在下面的示例中,我会想象injectHTML 返回一个包装器,该包装器会转换原始流的一部分——但我不知道如何到达那里。任何建议将不胜感激。

addEventListener("fetch", async event => {
    let request = event.request;
    if(request.method !== "GET" ||
            !request.headers.get("Accept").includes("text/html")) {
        return;
    }

    let response = await fetch(request);
    let stream = injectHTML(response.body);
    response = new Response(stream);
    event.respondWith(response);
});

【问题讨论】:

    标签: service-worker fetch-api


    【解决方案1】:

    Jake 的 streams explainer 及其包含的示例是一个很好的起点。

    最相关的例子是那个,呃...replaces "cloud" with "butt"

    Service Worker 代码以及一些注意事项,can be found on GitHub

    为了完整起见,这里是处理替换的相关服务工作者代码,但我建议在上下文中查看完整的源代码。

    function replaceResponse(response, bufferSize, match, replacer) {
      const reader = response.body.getReader();
      const encoder = new TextEncoder();
      const decoder = new TextDecoder();
      let bufferStr = '';
    
      const stream = new ReadableStream({
        pull: controller => {
          return reader.read().then(result => {
            if (result.done) {
              controller.enqueue(encoder.encode(bufferStr));
              controller.close();
              return;
            }
    
            const bytes = result.value;
            bufferStr += decoder.decode(bytes, {stream: true});
    
            // this is the end of the final replacement in the FINAL string
            let lastReplaceEnds = 0;
            let replacedLengthDiff = 0;
            bufferStr = bufferStr.replace(match, (...args) => {
              const matched = args[0];
              // offset is the offset in the original string, hence replacedLengthDiff
              const offset = args[args.length - 2];
              const replacement = replacer(...args);
    
              replacedLengthDiff += replacement.length - matched.length;
              lastReplaceEnds = offset + matched.length + replacedLengthDiff;
              return replacement;
            });
    
            const newBufferStart = Math.max(bufferStr.length - bufferSize, lastReplaceEnds);
            controller.enqueue(encoder.encode(bufferStr.slice(0, newBufferStart)));
            bufferStr = bufferStr.slice(newBufferStart);
          });
        },
        cancel: () => {
          reader.cancel();
        }
      });
    
      return new Response(stream, {
        headers: response.headers
      });
    }
    

    【讨论】:

    • 谢谢,这对我有用!不完全确定我之前做错了什么,但也许我只是迷失在这一切的陌生中。看起来我的示例也需要更少的代码,这意味着我开始识别所有移动部件。 PS:当然,我对 Jake 文章的描述只是为了表达最崇高的敬意;它也让我奉承!
    猜你喜欢
    • 1970-01-01
    • 2020-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多