获取响应流操作

时间:2018-06-03 07:41:06

标签: service-worker fetch-api

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

在返回缓存的HTML响应时,我想在{1}}之前和之后流式传输原始响应时注释<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);
});

1 个答案:

答案 0 :(得分:1)

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

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

服务工作者代码,以及一些警告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
  });
}