<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【總結(jié)】2104- 高效傳輸大的 JSON 數(shù)據(jù),流式處理真香!

          共 15979字,需瀏覽 32分鐘

           ·

          2024-07-16 21:31

          什么是 TextDecoder API

          TextDecoder[1] API 是一個用于將二進(jìn)制數(shù)據(jù)(通常是 ArrayBuffer 或 TypedArray)解碼為字符串的 JavaScript API。它是 Web 平臺的一部分,主要用于處理文本編碼的解碼工作。比如,從服務(wù)器接收到的流式數(shù)據(jù)、文件數(shù)據(jù)等。

          為什么使用 TextDecoder API

          在處理 Web 應(yīng)用中的二進(jìn)制數(shù)據(jù)時,通常需要將這些數(shù)據(jù)轉(zhuǎn)換為可讀的字符串格式。TextDecoder 提供了一種高效且便捷的方法來實(shí)現(xiàn)這一點(diǎn)。

          TextDecoder API 有以下特點(diǎn):

          • 高效性:比手動逐字節(jié)處理高效,能夠直接解碼為字符串。
          • 支持多種編碼:支持多種文本編碼(如 UTF-8、UTF-16、ISO-8859-1 等),提供了靈活性。
          • 支持流式處理:能夠逐塊處理數(shù)據(jù),適合處理大數(shù)據(jù)流或需要實(shí)時處理的數(shù)據(jù)。

          如何使用 TextDecoder API

          下面我們將介紹 TextDecoder API 的四種使用場景:

          1. 解碼 ArrayBuffer 數(shù)據(jù)
          2. 解碼不同編碼的二進(jìn)制數(shù)據(jù)
          3. 解碼流式 JSON 數(shù)據(jù)
          4. 解碼大 JSON 文件中的數(shù)據(jù)塊

          1.解碼 ArrayBuffer 數(shù)據(jù)

          // 創(chuàng)建一個包含 UTF-8 編碼文本的 Uint8Array
          const uint8Array = new Uint8Array([721011081081113287111114108100]);

          // 創(chuàng)建一個 TextDecoder 實(shí)例,默認(rèn)使用 UTF-8 編碼
          const decoder = new TextDecoder('utf-8');

          // 將 Uint8Array 解碼為字符串
          const decodedString = decoder.decode(uint8Array);

          console.log(decodedString); // 輸出 "Hello World"

          2.解碼不同編碼的二進(jìn)制數(shù)據(jù)

          // 使用不同的編碼創(chuàng)建 TextDecoder 實(shí)例
          const utf16Decoder = new TextDecoder('utf-16');
          const iso88591Decoder = new TextDecoder('iso-8859-1');

          // 示例二進(jìn)制數(shù)據(jù)
          const utf16Array = new Uint16Array([0x00480x00650x006C0x006C0x006F]);
          const iso88591Array = new Uint8Array([72101108108111]);

          // 解碼為字符串
          const utf16String = utf16Decoder.decode(utf16Array);
          const iso88591String = iso88591Decoder.decode(iso88591Array);

          console.log(utf16String); // 輸出 "Hello"
          console.log(iso88591String); // 輸出 "Hello"

          3.解碼流式 JSON 數(shù)據(jù)

          首先,我們先來看一下效果:

          在以上的示例中,我們使用 Node.js 的 http 模塊,快速搭建一個本地 SSE[2](Server-Sent Events)服務(wù)器。

          server.js

          const http = require("http");

          const PORT = 3000;

          const server = http.createServer((req, res) => {
            if (req.url === "/sse") {
              res.writeHead(200, {
                "Content-Type""text/event-stream",
                "Cache-Control""no-cache",
                Connection"keep-alive",
                "Access-Control-Allow-Origin""*",
                "Access-Control-Allow-Headers":
                  "Origin, X-Requested-With, Content-Type, Accept",
              });

              let id = 1;
              const interval = setInterval(() => {
                const data = {
                  id: id,
                  message`This is message ${id}`,
                  timestamp: +new Date(),
                };
                res.write(`data: ${JSON.stringify(data)}\n\n`);

                // 停止條件
                if (id == 5) {
                  res.write("event: end\n");
                  res.write("data: End of stream\n\n");
                  clearInterval(interval);
                  res.end();
                }

                id++;
              }, 1000);

              req.on("close", () => {
                clearInterval(interval);
              });
            } else {
              res.writeHead(404, { "Content-Type""text/plain" });
              res.end("404 Not Found");
            }
          });

          server.listen(PORT, () => {
            console.log(`Server is running on http://localhost:${PORT}`);
          });

          需要注意的是,在 sse 接口中,我們設(shè)置 Content-Type 響應(yīng)頭的類型為: "text/event-stream",告訴客戶端我們返回的是流式數(shù)據(jù)。

          index.html

          ??:訪問 index.html 網(wǎng)頁前,記得運(yùn)行 node server.js 命令先啟動 SSE 服務(wù)器。

          <!-- public/index.html -->
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>SSE & TextDecoder</title>
          </head>
          <body>
              <h1>Decode Server-Sent Events JSON Stream Data</h1>
              <div id="messages"></div>
              <script src="client.js"></script>
          </body>
          </html>

          client.js

          document.addEventListener("DOMContentLoaded", () => {
            const messagesDiv = document.querySelector("#messages");
            const textDecoder = new TextDecoder("utf-8");

            fetch("http://localhost:3000/sse").then((response) => {
              const reader = response.body.getReader();
              return new ReadableStream({
                start(controller) {
                  function push({
                    reader.read().then(({ done, value }) => {
                      if (done) {
                        controller.close();
                        return;
                      }

                      const chunk = textDecoder.decode(value, { streamtrue });
                      const lines = chunk.split("\n");

                      for (const line of lines) {
                        if (line.startsWith("data: ")) {
                          const json = line.slice(6);
                          const data = JSON.parse(json);
                          const p = document.createElement("p");
                          p.textContent = `ID: ${data.id}, Message: ${data.message}, Timestamp: ${data.timestamp}`;
                          messagesDiv.appendChild(p);
                        } else if (line.startsWith("event: end")) {
                          const p = document.createElement("p");
                          p.textContent = "End of stream";
                          messagesDiv.appendChild(p);
                          return;
                        }
                      }

                      push();
                    });
                  }

                  push();
                },
              });
            });
          });

          SSE 事件流是一個簡單的文本數(shù)據(jù)流,文本是使用 UTF-8 格式的編碼。所以,在創(chuàng)建 textDecoder 對象時,我們需要設(shè)置它的編碼為 utf-8。有了 textDecoder 對象后,調(diào)用它提供的 decode 方法就可以進(jìn)行解碼了。

          4.解碼大 JSON 文件中的數(shù)據(jù)塊

          當(dāng)遇到傳輸大 JSON 數(shù)據(jù)的時候,如果使用傳統(tǒng)的方式,我們需要接收完整的 JSON 數(shù)據(jù)后,才能開始進(jìn)行數(shù)據(jù)處理,這對用戶體驗(yàn)會造成一定的影響。為了解決這個問題,我們可以使用一些現(xiàn)成的 JSON Stream 解析庫。比如,@streamparser/json[3],該庫內(nèi)部也使用了本文介紹的 TextDecoder API。

          下面,我們先來看一下效果:

          上圖中輸出的 JSON 數(shù)據(jù)來自以下的 large.json 文件。我們對該文件按照 0.5KB 的大小進(jìn)行切割,然后每個 500ms 發(fā)送下一個數(shù)據(jù)塊。利用 @streamparser/json 這個庫,我們實(shí)現(xiàn)了解析 JSON 數(shù)據(jù)流(JSON Chunk)的功能。

          large.json

          [
            {},
            {
              "image": [
                {
                  "shape""rect",
                  "fill""#333",
                  "stroke""#999",
                  "x"0.5e1,
                  "y"0.5,
                  "z"0.8,
                  "w"0.5e5,
                  "u"2e10,
                  "foo"2e1,
                  "bar"2,
                  "width"47,
                  "height"47
                }
              ],
              "corners": { "1"true"3"true"7"true"9"true }
            },
           ...
          ]

          json-server.js

          const http = require("http");
          const { join } = require("path");
          const { readFileSync } = require("fs");

          const PORT = 3000;

          const largeJson = readFileSync(join(__dirname, "large.json")).toString();

          const server = http.createServer((req, res) => {
            if (req.url === "/stream-json") {
              res.writeHead(200, {
                "Content-Type""application/json",
                "Cache-Control""no-cache",
                Connection"keep-alive",
                "Access-Control-Allow-Origin""*",
                "Access-Control-Allow-Headers":
                  "Origin, X-Requested-With, Content-Type, Accept",
              });

              const CHUNK_SIZE = 512// 每塊 0.5KB
              let position = 0;

              const interval = setInterval(() => {
                const chunk = largeJson.slice(position, position + CHUNK_SIZE);
                res.write(chunk);
                position += CHUNK_SIZE;

                if (position >= largeJson.length) {
                  clearInterval(interval);
                  res.end();
                }
              }, 500); // 每 500ms 發(fā)送一塊數(shù)據(jù)

              req.on("close", () => {
                clearInterval(interval);
              });
            } else {
              res.writeHead(404, { "Content-Type""text/plain" });
              res.end("404 Not Found");
            }
          });

          server.listen(PORT, () => {
            console.log(`Server is running on http://localhost:${PORT}`);
          });

          stream-json.html

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Stream JSON</title>
            </head>
            <body>
              <h1>Stream JSON</h1>
              <div id="messages"></div>
              <script type="module">
                import { JSONParser } from "https://cdn.jsdelivr.net/npm/@streamparser/[email protected]/+esm";
                const messagesDiv = document.querySelector("#messages");

                document.addEventListener("DOMContentLoaded"async () => {
                  const parser = new JSONParser();

                  const response = await fetch("http://localhost:3000/stream-json");

                  const reader = response.body.pipeThrough(parser).getReader();
                  while (true) {
                    const { done, value: parsedElementInfo } = await reader.read();
                    if (done) break;

                    const { value, key, parent, stack, partial } = parsedElementInfo;
                    if (partial) {
                      console.log(`Parsing value: ${value}... (still parsing)`);
                    } else {
                      const p = document.createElement("p");
                      p.textContent = `${JSON.stringify(value)}`;
                      messagesDiv.appendChild(p);
                      console.log(`Value parsed: ${JSON.stringify(value)}`);
                    }
                  }
                });
              
          </script>
            </body>
          </html>

          @streamparser/json 這個庫還有其他的用法,如果你感興趣的話,可以看一下它的使用文檔。TextDecoder API 的相關(guān)內(nèi)容就介紹到這里,如果你有其它好玩、有用的使用場景,可以給我留言哈。

          參考資料
          [1]

          TextDecoder: https://developer.mozilla.org/zh-CN/docs/Web/API/TextDecoder

          [2]

          SSE: https://developer.mozilla.org/zh-CN/docs/Web/API/Server-sent_events

          [3]

          @streamparser/json: https://www.npmjs.com/package/@streamparser/json

          瀏覽 94
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  熟女操逼视频 | 五月网婷婷| 高清毛片AAAAAAAAA郊外 | 玖玖资源站中文字幕 | 亚州国产黄色电影视频 |