簡體   English   中英

使用 Vanilla JS 從 ReadableStream 解析 JSON 數據塊

[英]Parse JSON data chunk from ReadableStream with Vanilla JS

我正在獲取一個大的 JSON 文件(200mb),我想將該數據作為數據流呈現出來。我遇到的問題是,在我解碼並解析流向我的塊之后,返回語法錯誤我在控制台Unexpected end of JSON input 我想要做的是解析返回的塊,並在我得到它后立即對這些數據做一些事情。 但是,由於 ReadableStream 以無法預測的切片方式流式傳輸,因此我無法對返回值執行 JSON.parse() 。 需要進行什么樣的數據按摩才能實現這一目標? 有更好的方法嗎?

這是我的代碼:

const decoder = new TextDecoder('utf-8')
fetch("../files/response.json")
    .then(response => {
        const reader = response.body.getReader()
        new ReadableStream({
            start(controller) {
                function enqueueValues() {
                    reader.read()
                    .then(({ done, value }) => {
                        if (done) {
                            controller.close() // stream is complete
                            return
                        }
                        var decodedValue = decoder.decode(value) // one chunk of invalid json data in string format

                        console.log(JSON.parse(decodedValue)) // json syntax error

                        // do something with the json value here

                        controller.enqueue(value)

                        enqueueValues() // run until all data has been streamed
                    })
                }
                enqueueValues()
            }
        })
    })

我認為實現這一點的唯一方法是在每個塊中發送有效的 json 數據(對象,數組)。

這是一個示例 express.js 處理程序:

app.get("/stream", (req, res) => {
  let i = 0;
  const interval = setInterval((a) => {
    i += 1;
    res.write(JSON.stringify([{ message: `Chunk ${i}` }]));
  }, 500);

  setTimeout(() => {
    clearInterval(interval);
    res.end(() => {
      console.log("End");
    });
  }, 5000);
});

這樣做的缺點是最終的 json (所有塊連接成一個字符串)無效。 但是在瀏覽器的 memory 中保存 200mb object 也不好。

更新:我試圖解決我項目中的類似問題並找到了解決方法。

  • 只需將我所有的塊(對象)包裝到一個數組中
  • 將左方括號和右方括號作為單獨的塊發送
  • 在每個數據塊的末尾添加逗號。

然后在客戶端我忽略等於[]的塊,並在每個數據塊中剪切結束逗號。

服務器:

app.get("/stream", (req, res) => {
  let i = 0,
    chunkString;
  res.write("["); // <<---- OPENING bracket
  const interval = setInterval((a) => {
    i += 1;
    chunkString = JSON.stringify({ message: `Chunk ${i}` });
    res.write(`${chunkString},`);   // <<----- Note ending comma at the end of each data chunk
  }, 500);

  setTimeout(() => {
    clearInterval(interval);
    res.end("]", () => {. // <<---- CLOSING bracket
      console.log("End");
    });
  }, 5000);
});

客戶:

const decoder = new TextDecoder("utf-8");

const handleJsonChunk = (jsonChunk) => {
  console.log("Received Json Chunk: ", jsonChunk);
};

const main = async () => {
  const response = await fetch("http://localhost:3000/stream");
  const reader = response.body.getReader();
  const skipValues = ["[", "]"];

  const work = (reader) => {
    reader.read().then(({ done, value }) => {
      if (!done) {
        let stringValue = decoder.decode(value);
        const skip = skipValues.indexOf(stringValue) >= 0;
        if (skip) return work(reader);

        if (stringValue.endsWith(","))
          stringValue = stringValue.substr(0, stringValue.length - 1);

        try {
          const jsonValue = JSON.parse(stringValue);
          handleJsonChunk(jsonValue);
        } catch (error) {
          console.log(`Failed to parse chunk. Error: ${error}`);
        }

        work(reader);
      }
    });
  };
  work(reader);
};

main();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM