Передача текстового потока из NodeJS в браузер - PullRequest
1 голос
/ 07 октября 2019

Я пытаюсь передать текстовый файл, обработанный в NodeJS, в браузер.
Ниже приведен текстовый файл перед обработкой.
Файл называется dbUsers.json.

{"userId":443,"email":"bob@gmail.com","hashedPassword":"36583a77a098c02ef111e2f2521d77b58e420f2bc7e9bf930ec24b21d42ea2e0","timeStamp":1567439821109,"deleted":false}
{"userId":447,"email":"alice@gmail.com","hashedPassword":"36583a77a098c02ef111e2f2521d77b58e420f2bc7e9bf930ec24b21d42ea2e0","timeStamp":1567439909013,"deleted":false}
{"userId":451,"email":"cliff@gmail.com","hashedPassword":"36583a77a098c02ef111e2f2521d77b58e420f2bc7e9bf930ec24b21d42ea2e0","timeStamp":1567443638340,"deleted":false}
...

После обработки я могу передать данные в новый файл на сервере NodeJS с помощью следующих команд:

// Create a writable stream and specify the file which will receive the data from the readable stream.
let destinationStream = fs.createWriteStream(_data.baseDir + '/dbPermissions/dbUsers' + '/' + 'test' + '.txt', {flags : 'a'});


pipeline
(
  sourceStream,
  destinationStream,
  function(error){if(error){console.log('There was an error.');}}
);

В новом файле отображаются данные, обработанные, как и ожидалось.
Некоторые поля были удалены, а записи, помеченные для удаления, были удалены.
Это показывает, что sourceStream работает в NodeJS.
Теперь данные в новом файле выглядят следующим образом:

{"userId":443,"email":"bob@gmail.com","timeStamp":1567439821109}
{"userId":447,"email":"alice@gmail.com","timeStamp":1567439909013}
{"userId":451,"email":"cliff@gmail.com","timeStamp":1567443638340}
...

Запись sourceStream на консоль NodeJS перед потоковой передачей в клиентский браузер приводит к следующему выводу.

Readable {
  _readableState:
   ReadableState {
     objectMode: false,
     highWaterMark: 16384,
     buffer: BufferList { head: [Object], tail: [Object], length: 45 },
     length: 3035,
     pipes:
      WriteStream {
        _writableState: [WritableState],
        writable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 6,
        _maxListeners: undefined,
        path:
         'C:\\Users\\user\\Desktop\\Tutorials\\iotajs\\ias\\accounting\\/dbPermissions/dbUsers/test.txt',
        fd: null,
        flags: 'a',
        mode: 438,
        start: undefined,
        autoClose: true,
        pos: undefined,
        bytesWritten: 0,
        closed: false },
     pipesCount: 1,
     flowing: true,
     ended: true,
     endEmitted: false,
     reading: false,
     sync: true,
     needReadable: false,
     emittedReadable: true,
     readableListening: false,
     resumeScheduled: true,
     paused: false,
     emitClose: true,
     destroyed: false,
     defaultEncoding: 'utf8',
     awaitDrain: 0,
     readingMore: true,
     decoder: null,
     encoding: null },
  readable: true,
  domain: null,
  _events:
   [Object: null prototype] {
     close: [ [Function], [Function: onclose] ],
     end: [ [Function: onend], [Function] ],
     finish: [Function: onfinish],
     error: [Function: onerror],
     data: [Function: ondata] },
  _eventsCount: 5,
  _maxListeners: undefined }
Returning this response:  200
Returning this response:  200

При потоковой передаче sourceStream в браузер и последующем выходе из консоли браузера вывод такой же. как указано выше.
Так что я уверен, что sourceStream доходит до клиента без изменений.

Данные, с которыми мне нужно работать, вероятно, заблокированы во многих свойствах буфера объекта выше, теперь называемого responseTextStream в клиентском браузере.

Моя проблема в том, что я не знаю, как получить доступ к информации в буферах, и при этом я не знаю, как преобразовать их из чисел обратно в текст.

Ниже приведена функция в клиентском браузере, где я надеюсь использовать данные в буферах. Здесь мне нужна помощь - я не знаю, как получить доступ к потоку. Спасибо, Джон

// Populate the dbUsersList webpage with user records.
app.loadUsersListPage = function()
{  
  // Ask the server for the JSON records found in the dbUsers file.
  // Then run the callback function defined here which inserts rows into the usersListTable on the webpage
  // and populates them with data from the file of JSON records returned.
  app.client.request(undefined,'api/aUsers','GET',QueryStringObject,undefined,function(statusCode,responseTextStream)
  {
    // if the call to handlers._users.get which is mapped to api/aUsers called back success.
    if(statusCode == 200) 
    {
      // The streamed data can be seen on the console as a buffer full of numbers
      console.log(responseTextStream._readableState.buffer.head.data.data);

      // Create a handle which can be used to manipulate the table on the webpage.
      var table = document.getElementById("usersListTable");


      // The pseudocode below does not work but is shows what I hope to accomplish. 
      // The line below does not help to access the stream. This is where I need help.
      // What line or lines of code would facilitate access to the stream and allow 
      // processing it as a string, character by character, as shown below.
      var Astr = responseTextStream;

      var line = "";

      for(var i=0; i<Astr.length; i++)
      {
        var chr = String.fromCharCode(Astr[i]);
        if(chr == "\n" || chr == "\r")
        {
          // Look at each line of json at the console as it is consumed.
          console.log("line: ",line);

          // Turn the line, which is a json string, back into a json object 
          var recordObject = JSON.parse(line);

          if(recordObject)
          {
            // Insert a new row in the table.
            var tr = table.insertRow(-1);
            // Make the new row a member of the class 'checkRow'
            tr.classList.add('checkRow');

            // Insert five new cells into the new row.
            var td0 = tr.insertCell(0);
            var td1 = tr.insertCell(1);
            var td2 = tr.insertCell(2);   
            var td3 = tr.insertCell(3);          

            // load the new cells with data from the recordObject.
            td0.innerHTML = recordObject.userId;      
            td1.innerHTML = recordObject.email;
            td2.innerHTML = recordObject.timeStamp;      
            td3.innerHTML = '<a href="/users/edit?email=' + recordObject.userId + '">View / Edit / Delete</a>';
          } // End of: if(recordObject)

          // clear the line buffer to start the next line.
          line = "";

        } // End of: if(chr == "\n" || chr == "\r"){do stuff}
        else 
        {
            line += chr;
        }           

      }; // End of: for(var i=0; i<Astr.length; i++){...}

    } // End of: if the call to handlers._users.get which is mapped to api/aUsers called back successfully.

  }); // End of: app.client.request(undefined,'api/checks','GET'...

} // End of: app.loadUsersListPage = function(){...}
// End of: Populate the dbUsersList webpage with user records.

Согласно ответу @Brad, я использовал его код с таким же неудовлетворительным результатом. Ниже приведен код Брэда, а ниже - объект, который возвращается его кодом в тот момент, когда value регистрируется на консоли. Это тот же объект, что и раньше, с буферами, расширенными для проверки. Моя проблема остается в том, что возвращаемый объект - это просто набор буферов, которые содержат набор чисел, а не строк, с которыми я могу что-то сделать. При входе в консоль value я надеюсь увидеть каждую строку измененных данных в удобочитаемом формате. Что я не делаю или что я делаю не так? Спасибо, Джон

// Populate the dbUsersList webpage with user records.
app.loadUsersListPage = async function()
{  
  // Define which users will be retrieved from dbUsers.json
  // This is not being used for now so all records will be retrived.
  var QueryStringObject = {};

  // Define a client function that calls for data from the server.
  const fetchPromise = fetch('api/aUsers')
  .then
  (
    (res) => 
    {
      // Verify that we have some sort of 2xx response that we can use
      if (!res.ok) 
      {
        throw res;
      }

      // If no content, immediately resolve, don't try to parse JSON
      if (res.status === 204) 
      {
        return;
      }

      // Initialize variable to hold chunks of data as they come across.
      let textBuffer = '';

      // This does not seem to be used. Delete this after everything else is working.
      const self = this;

      // Process the stream.
      return res.body

      // Decode as UTF-8 Text
      .pipeThrough
      (
        new TextDecoderStream()
      )

      // Split on lines
      .pipeThrough
      (
        new TransformStream
        (
          {
            transform(chunk, controller) 
            {
              textBuffer += chunk;

              const lines = textBuffer.split('\n');

              for (const line of lines.slice(0, -1)) 
              {
                controller.enqueue(line);
              } // End of: for (const line ...)

              textBuffer = lines.slice(-1)[0];
            }, // End of: Transform(chunk, controller){do stuff}

            flush(controller) 
            {
              if (textBuffer) 
              {
                controller.enqueue(textBuffer);
              } // End of: if (textBuffer)
            } // End of: flush(controller){do stuff}
          } // End of: parameters for new TransformStream
        ) // End of: call to constructor new TransformStream
      ) // End of: parameters for pipeThrough - Split on lines

      // Parse JSON objects
      .pipeThrough
      (
        new TransformStream
        (
          {
            transform(line, controller) 
            {
              if (line) 
              {
                controller.enqueue
                (
                  JSON.parse(line)
                ); //End of: call to controller.enqueue function
              } // End of: if (line)
            } // End of: transform function
          } // End of: parameter object for new TransformStream
        ) // End of: new TransformStream parameters
      ); // End of: parameters for .pipeThrough - Parse JSON objects
    } // End of: .then callback function instruction for fetch
  ); // End of: .then callback parameters for fetch


  // Call to function which asks server for data.
  const res = await fetchPromise;

  const reader = res.getReader();

  function read() 
  {
    reader.read()
    .then
    (
      ({value, done}) => 
      {
        if (value) {
          // Your object will be here
          console.log('I got to this point');
          console.log(value);
        }
        if (done) {
          return;
        }
        read();
      }
    );
  }

  read();

} // End of: app.loadUsersListPage = function(){...}
// End of: Populate the dbUsersList webpage with user records.

Это то, что я получаю, когда код Брэда записывает value на консоль. Это то же самое, что я получил раньше. Я надеялся увидеть строки текста. Что я не делаю или что я делаю не так? Спасибо, Джон

{_readableState: {…}, readable: true, domain: null, _events: {…}, _eventsCount: 0}
domain: null
readable: true
_events: {}
_eventsCount: 0
_readableState:
awaitDrain: 0
buffer:
head:
data:
data: (65) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 52, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 111, 98, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 51, 57, 56, 50, 49, 49, 48, 57, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 52, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 97, 108, 105, 99, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 51, 57, 57, 48, 57, 48, 49, 51, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 53, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 99, 108, 105, 102, 102, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 51, 54, 51, 56, 51, 52, 48, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 53, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 97, 114, 103, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 51, 55, 54, 53, 54, 48, 57, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 53, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 99, 108, 105, 110, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 51, 56, 49, 51, 49, 54, 55, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 54, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 121, 114, 111, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 51, 57, 52, 50, 48, 57, 54, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 54, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 105, 108, 116, 111, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 52, 50, 48, 55, 53, 52, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 55, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 97, 114, 110, 111, 108, 100, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 52, 52, 50, 51, 57, 55, 53, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 55, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 115, 97, 108, 108, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 52, 56, 57, 56, 57, 52, 54, 57, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 57, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 97, 114, 118, 105, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 53, 48, 51, 56, 50, 52, 54, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 57, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 110, 97, 110, 99, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 53, 48, 52, 49, 55, 52, 52, 57, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 52, 57, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 102, 114, 101, 100, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 53, 48, 52, 51, 48, 55, 52, 50, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 48, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 97, 108, 101, 120, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 52, 53, 52, 56, 55, 57, 55, 53, 51, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 48, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 104, 101, 114, 109, 97, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 53, 48, 54, 48, 50, 55, 50, 49, 55, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (69) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 49, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 97, 114, 103, 114, 101, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 53, 53, 55, 53, 50, 49, 51, 55, 51, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (69) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 49, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 115, 121, 110, 116, 104, 105, 97, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 53, 53, 55, 53, 55, 51, 56, 52, 48, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 49, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 99, 111, 110, 110, 101, 114, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 53, 53, 55, 54, 48, 55, 51, 56, 55, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 50, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 97, 114, 103, 111, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 55, 53, 53, 56, 53, 57, 57, 50, 48, 48, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 50, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 100, 101, 110, 105, 115, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 48, 54, 54, 50, 56, 55, 53, 54, 48, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 51, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 112, 104, 105, 108, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 48, 54, 54, 56, 49, 50, 48, 55, 48, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 51, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 111, 114, 103, 97, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 48, 55, 55, 53, 55, 55, 56, 53, 56, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 51, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 108, 117, 107, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 48, 55, 50, 53, 48, 49, 51, 55, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 52, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 119, 97, 114, 114, 101, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 48, 56, 52, 55, 50, 54, 56, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 53, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 119, 105, 108, 98, 117, 114, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 49, 49, 49, 56, 54, 51, 55, 54, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 53, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 112, 97, 117, 108, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 49, 49, 50, 50, 52, 55, 57, 49, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (70) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 53, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 102, 108, 111, 114, 97, 110, 99, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 53, 50, 50, 48, 48, 51, 50, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (69) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 54, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 117, 109, 102, 111, 114, 100, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 53, 50, 51, 55, 53, 57, 56, 50, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 54, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 97, 114, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 53, 55, 55, 48, 49, 57, 55, 49, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 55, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 104, 111, 109, 101, 114, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 53, 55, 55, 57, 48, 53, 49, 53, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (69) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 55, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 102, 97, 108, 107, 110, 101, 114, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 54, 51, 48, 52, 55, 52, 53, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (70) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 55, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 97, 110, 100, 101, 114, 115, 111, 110, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 57, 54, 51, 56, 53, 50, 53, 51, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 56, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 97, 114, 114, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 49, 57, 55, 55, 52, 54, 48, 55, 51, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 56, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 97, 114, 110, 101, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 50, 51, 54, 54, 52, 54, 54, 54, 50, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 57, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 101, 116, 116, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 50, 51, 54, 55, 52, 57, 54, 51, 54, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 57, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 115, 116, 101, 118, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 50, 52, 52, 56, 49, 56, 48, 50, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 53, 57, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 115, 104, 105, 114, 108, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 50, 56, 49, 54, 49, 54, 50, 53, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 48, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 97, 103, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 50, 56, 49, 56, 57, 54, 52, 50, 55, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (71) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 48, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 99, 111, 110, 115, 116, 97, 110, 99, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 50, 48, 54, 56, 49, 53, 54, 54, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (68) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 49, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 109, 97, 114, 115, 104, 97, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 50, 48, 56, 55, 55, 57, 56, 54, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 49, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 103, 114, 101, 103, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 50, 49, 49, 53, 55, 49, 56, 53, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 50, 51, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 112, 101, 116, 101, 114, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 50, 49, 50, 48, 55, 55, 51, 55, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (67) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 50, 55, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 99, 105, 110, 100, 121, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 56, 52, 52, 52, 49, 52, 54, 49, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 51, 49, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 114, 111, 115, 101, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 56, 52, 52, 55, 50, 49, 48, 57, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (66) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 51, 53, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 98, 97, 114, 98, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 56, 52, 53, 51, 57, 52, 50, 52, 125, 10]
type: "Buffer"
__proto__: Object
next:
data:
data: (69) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 51, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 103, 105, 108, 98, 101, 114, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 56, 52, 53, 57, 56, 56, 54, 55, 125, 10]
type: "Buffer"
__proto__: Object
next: null

length: 45
tail:
data:
data: (69) [123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 54, 51, 57, 44, 34, 101, 109, 97, 105, 108, 34, 58, 34, 103, 105, 108, 98, 101, 114, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 34, 44, 34, 116, 105, 109, 101, 83, 116, 97, 109, 112, 34, 58, 49, 53, 54, 56, 52, 56, 52, 53, 57, 56, 56, 54, 55, 125, 10]
type: "Buffer"
__proto__: Object
next: null

I did not include the entire object.

Как видно выше, в возвращаемом объекте содержится много буферов. Я думаю, что каждый буфер содержит строку / запись из исходного файла dbUsers с некоторыми удаленными полями и удаленными дублирующимися записями. Я хотел бы видеть эти буферы как текст, читаемый человеком. Поскольку возвращается много буферов, в конце концов мне нужно будет научиться перебирать буферы, но сейчас я просто пытаюсь превратить первый буфер в текст. Брэд в этот момент находится в невыгодном положении, потому что его нет рядом с компьютером. Поэтому он не может видеть код, который я использую. В любом случае, он любезно посоветовал мне попробовать функцию toString (). Поэтому я попробовал следующую строку кода на первом буфере. console.log(value._readableState.buffer.head.data.data.toString()); Следующие цифры были результатом. Я надеялся на строку текста. Кто-нибудь может увидеть, что я не могу сделать? Спасибо, Джон

123,34,117,115,101,114,73,100,34,58,52,52,51,44,34,101,109,97,105,108,34,58,34,98,111,98,64,103,109,97,105,108,46,99,111,109,34,44,34,116,105,109,101,83,116,97,109,112,34,58,49,53,54,55,52,51,57,56,50,49,49,48,57,125,10

После попытки следующей строки кода я все еще получаю цифры, а не буквы на консоли. console.log(value._readableState.buffer.head.data.data.toString('utf8')); Спасибо, Джон

Ответы [ 2 ]

1 голос
/ 07 октября 2019

Поскольку вы используете Chrome, вы можете использовать все новые возможности, такие как TextDecoderStream и TransformStream, в изящном конвейере, который передает данные из ответа HTTP и декодирует этот JSON с разделителями строк, как и он. Проверьте это:

const fetchPromise = fetch(url, params).then((res) => {
  // Verify that we have some sort of 2xx response that we can use
  if (!res.ok) {
    throw res;
  }

  // If no content, immediately resolve, don't try to parse JSON
  if (res.status === 204) {
    return;
  }

  let textBuffer = '';

  const self = this;

  return res.body
    // Decode as UTF-8 Text
    .pipeThrough(new TextDecoderStream())

    // Split on lines
    .pipeThrough(new TransformStream({
      transform(chunk, controller) {
        textBuffer += chunk;
        const lines = textBuffer.split('\n');
        for (const line of lines.slice(0, -1)) {
          controller.enqueue(line);
        }
        textBuffer = lines.slice(-1)[0];
      },
      flush(controller) {
        if (textBuffer) {
          controller.enqueue(textBuffer);
        }
      }
    }))

    // Parse JSON objects
    .pipeThrough(new TransformStream({
      transform(line, controller) {
        if (line) {
          controller.enqueue(
            JSON.parse(line)
          );
        }
      }
    }));
});

Теперь вы можете использовать этот новый поток объектов, как и любой другой:

  const res = await fetchPromise;
  const reader = res.getReader();
  function read() {
    reader.read().then(({value, done}) => {
      if (value) {
        // Your object will be here
      }
      if (done) {
        return;
      }
      read();
    });
  }
  read();

(Примечание: я не проверял этот код в этом примере контекста. .. Я изменил это в одном из моих проектов, поэтому, пожалуйста, просмотрите его и адаптируйте для своих конкретных целей.

0 голосов
/ 19 октября 2019

JavaScript Брэда сверху, который работает в браузере, работал отлично.
Проблема заключалась в том, как поток был отправлен с сервера NodeJS в браузер.
Весь рабочий код был перенесен на github.
Нажмите здесь, чтобы увидеть полный репозиторий, который не использует никаких зависимостей.

Действие начинается здесь с функции loadUsersListPage
Функция приведена ниже для удобства.
Это код Брэда, который запрашивает поток текста с сервера, а затем использует этот поток для заполнения веб-страницы списком пользователей из таблицы dbUsers на сервере.

// Populate the dbUsersList webpage with user records.
app.loadUsersListPage = async function()
{  
  // Create a handle which can be used to manipulate the table on the webpage.
  var table = document.getElementById("usersListTable");  

  // Define which users will be retrieved from dbUsers.json
  // This is not being used for now so all records will be retrived.
  var QueryStringObject = {};

  // Define a client function that calls for data from the server.
  const fetchPromise = fetch('api/aUsers')
  .then
  (
    (res) => 
    {
      // Verify that we have some sort of 2xx response that we can use
      if (!res.ok) 
      {
        // throw res;
        // Show 'you have no checks' message
        document.getElementById("noChecksMessage").style.display = 'table-row';

        // Show the createCheck CTA
        document.getElementById("createCheckCTA").style.display = 'block';

        console.log("Error trying to load the list of users: ");        
      }

      // If no content, immediately resolve, don't try to parse JSON
      if (res.status === 204) 
      {
        return;
      }

      // Initialize variable to hold chunks of data as they come across.
      let textBuffer = '';

      // Process the stream.
      return res.body

      // Decode as UTF-8 Text
      .pipeThrough
      (
        new TextDecoderStream()
      )

      // Split on lines
      .pipeThrough
      (
        new TransformStream
        (
          {
            transform(chunk, controller) 
            {
              textBuffer += chunk;            

              // Split the string of records on the new line character and store the result in an array named lines.
              const lines = textBuffer.split('\n');

              // Cycle through all elements in the array except for the last one which is only holding a new line character.
              for (const line of lines.slice(0, -1))
              {
                // Put the element from the array into the controller que.
                controller.enqueue(line);
              } // End of: for (const line ...)

              // Put the last element from the array (the new line character) into the textBuffer but don't put it in the que.
              textBuffer = lines.slice(-1)[0];             
            }, // End of: Transform(chunk, controller){do stuff}

            flush(controller) 
            {
              if (textBuffer) 
              {
                controller.enqueue(textBuffer);
              } // End of: if (textBuffer)
            } // End of: flush(controller){do stuff}
          } // End of: parameters for new TransformStream
        ) // End of: call to constructor new TransformStream
      ) // End of: parameters for pipeThrough - Split on lines

      // Parse JSON objects
      .pipeThrough
      (
        new TransformStream
        (
          {
            transform(line, controller) 
            {
              if (line) 
              {
                controller.enqueue
                (
                  JSON.parse(line)
                ); //End of: call to controller.enqueue function
              } // End of: if (line)
            } // End of: transform function
          } // End of: parameter object for new TransformStream
        ) // End of: new TransformStream parameters
      ); // End of: parameters for .pipeThrough - Parse JSON objects
    } // End of: .then callback function instruction for fetch
  ); // End of: .then callback parameters for fetch


  // Call to function which asks server for data.
  const res = await fetchPromise;

  const reader = res.getReader();

  function read() 
  {
    reader.read()
    .then
    (
      ({value, done}) => 
      {
        if (value) 
        {
          // Your object (value) will be here

          // Insert a new row in the table.
          var tr = table.insertRow(-1);
          // Make the new row a member of the class 'checkRow'
          tr.classList.add('checkRow');

          // Insert five new cells into the new row.
          var td0 = tr.insertCell(0);
          var td1 = tr.insertCell(1);
          var td2 = tr.insertCell(2);   
          var td3 = tr.insertCell(3);          

          // load the new cells with data from the recordObject.
          td0.innerHTML = value.userId;      
          td1.innerHTML = value.email;
          td2.innerHTML = value.timeStamp;      
          td3.innerHTML = '<a href="/users/edit?email=' + value.userId + '">View / Edit / Delete</a>';
        } // End of: if(value){do stuff}

        if (done) {return;}

        read();

        // Show the createCheck CTA
        document.getElementById("createCheckCTA").style.display = 'block';
      } // End of: if a record object (value) is returned.
    ); // End of: .then callback after read function completes.
  } // End of: function definition: function read(){do stuff}

  // Call the read function defined above.
  read();

} // End of: app.loadUsersListPage = function(){...}
// End of: Populate the dbUsersList webpage with user records.

Рабочий код сервера находится здесь под функцией с именем unifiedServer
Для удобства эта функция приведена непосредственно ниже.
Эта функция направляет запрос кобработчик (также перечисленный ниже), а затем отправляет поток возвращенных данных обратно в браузер для использования кода Брэда.
В нижней части функции вы можете видеть, что конвейер используется для отправки потока в браузер.
Это то, чего мне не хватало.

// Define a function to route requests from the client to the handler and to serve back a response.
// All the logic for both the http and https server
server.unifiedServer = function(req, res)
{
  // Get the URL and parse it.
  var parsedUrl = url.parse(req.url, true);

  // Get the path from the URL.
  var path = parsedUrl.pathname;
  var trimmedPath = path.replace(/^\/+|\/+$/g, '');

  // Get the query string as an object.
  var queryStringObject = parsedUrl.query;

  // Get the http method.
  var method = req.method.toLowerCase(); 

  // Get the headers as an Object
  var headers = req.headers;

  // Instantiate decoder that will turn payload buffer into a string.
  var decoder = new StringDecoder('utf8');

  // Create an empty string for the request payload. 
  // It's called buffer but it is not a JavaScript buffer (not binary data) - it's just an empty string.
  // We are going to use it to hold the request payload buffer after it has been decoded and turned into a string.
  var buffer = '';


  // Call to event emitter req.on('data...
  // Watch for a chunk of data from the client's request payload buffer. 
  // The run the callback defined below with the returned data.
  req.on('data', function(data)
  {
    // Decode the chunk and write it to the payload string.
    buffer += decoder.write(data);
  });


  // Call to event emitter req.on('end...    
  // Watch for the end of the payload from the client request.
  // The callback defined here is the action taken after the entire request has been received.
  req.on('end', function()
  {
    // Finish writing to the buffer.
    buffer += decoder.end();


    // Choose the handler the client's request should go to.
    // If one is not found, use the notFound handler.
    // To be clear: A key in the router object below should match the request from the client.
    // chosenHandler becomes an alias for the handler function which is mapped to the key in the router object.
    // This is how we can refer to the handler function without knowing what it is in advance.
    var chosenHandler = typeof(server.router[trimmedPath]) !== 'undefined' ? server.router[trimmedPath] : aHandlers.notFound;

    // If the request is within the public directory, use the public handler instead of what was assigned by the line above.
    // The line below is required because the line above will only match a handler to a client request if
    //   the request exactly matches one of the keys in the request router object at the bottom of this file. 
    //   So if the request is "public" then we have a match with the public handler. But request 
    //   public/app.css would not be matched with a handler.
    chosenHandler = trimmedPath.indexOf('public/') > -1 ? handlers.public : chosenHandler;

    // Construct the data object to send to the handler.
    var data = 
    {
        'trimmedPath' : trimmedPath,
        'queryStringObject' : queryStringObject,
        'method' : method,
        'headers' : headers,
        'payload' : aHelpers.parseJsonToObject(buffer) 
    }; // End of: Construct the data object to send to the handler.

    // Call the handler specified by the client. 
    // Then execute the callback we are passing in as an argument.
    chosenHandler(data, function(statusCode, payload, contentType)
    {
      // Determine the type of response - default to json.
      contentType = typeof(contentType) == 'string' ? contentType : 'json';

      // Use the status code called back by the handler, or default to 200.
      statusCode = typeof(statusCode) == 'number' ? statusCode : 200;


      // Return the response parts that are content specific.
      var payloadString = '';

      if(contentType == 'json')
      {
        res.setHeader('Content-Type', 'application/json');    

        // Use the payload called back by the handler, or default to an empty object.
        payload = typeof(payload) =='object' ? payload : {};     

        // Convert the payload to a string.
        payloadString =  JSON.stringify(payload);       
      }

      if(contentType == 'html')
      {
        res.setHeader('Content-Type', 'text/html');       
        payloadString = typeof(payload) == 'string' ? payload : '';   
      }     

      if(contentType == 'favicon')
      {
        res.setHeader('Content-Type', 'image/x-icon');       
        payloadString = typeof(payload) !== 'undefined' ? payload : '';   
      }  

      if(contentType == 'css')
      {
        res.setHeader('Content-Type', 'text/css');       
        payloadString = typeof(payload) !== 'undefined' ? payload : '';   
      }  

      if(contentType == 'png')
      {
        res.setHeader('Content-Type', 'image/png');       
        payloadString = typeof(payload) !== 'undefined' ? payload : '';   
      }  

      if(contentType == 'jpg')
      {
        res.setHeader('Content-Type', 'image/jpeg');       
        payloadString = typeof(payload) !== 'undefined' ? payload : '';   
      }  

      if(contentType == 'plain')
      {
        res.setHeader('Content-Type', 'text/plain');       
        payloadString = typeof(payload) !== 'undefined' ? payload : '';
      }       

      // Return the response parts that are common to all content-types.      

      res.writeHead(statusCode); 

      if (contentType == 'stream')
      {
        pipeline
        (
          payload,
          res,
          function(error){if(error){console.log('There was an error.');}}
        );
      }
      else
      {
        res.end(payloadString);
      }

      console.log('Returning this response: ', statusCode);
    }); // End of: call to chosenHandler(...
  }); // End of: call to req.on('end', function(...
}; // End of: var unifiedServer = function(...
// End of: Define a function to route requests from the client to the handler and to serve back a response.

Обработчикфункция, которая:
читает данные из файла dbUsers.json,
обрабатывает данные,
и возвращает их в виде потока
можно найти здесь под именем handlers._users.get
Для удобства функция приведена ниже.

handlers._users.get = function(data, callback)
{
  // Create an empty map data structure which will be used to merge user records with the same email address.
  let usersMap = new Map();

  // This function sets up a stream where each chunk of data is a complete line in the dbUsers file.
  let readInterface = readline.createInterface
  (
    { // specify the file to be read.
      input: fs.createReadStream(_data.baseDir + '/dbPermissions/dbUsers' + '/' + 'dbUsers' + '.json'),
    }
  );


  // Look at each record in the file.
  readInterface.on('line', function(line) 
  {
    // Convert the JSON string (a single line from the dbUsers file) into lineValueObject.
    // These objects will written back to a new file after deleting some un-needed key/value pairs.
    let lineValueObject = JSON.parse(line);       

    // Declare a variable to serve as a key in the map to manage the lineValueObject.
    let userId = lineValueObject.userId;          

    if(lineValueObject.deleted === true)
    {
      // Remove this record from the map 
      usersMap.delete(userId);
    }
    else // this record has not been marked for deletion.
    {
      // Remove the hashed password key/value pair from the lineValueObject before returning it to the requester.
      delete lineValueObject.hashedPassword; 

      // Remove the deleted key/value pair from the lineValueObject before returning it to the requester.
      delete lineValueObject.deleted;            

      // Update this record in the map.
      usersMap.set(userId, lineValueObject);
    }

  }); // End of: readInterface.on('line', function(line){...}
  // End of: Look at each record...


  // This listener fires after we have looked through all the records in the dbUsers file.
  // The callback function defined here will stream the list of users back to the clients browser.
  readInterface.on('close', function() 
  {          
    // This readable stream will be used to write the result of the merge to a new file.
    const sourceStream = new Readable(); 

    for (const [key, valueObject] of usersMap)
    {
      // Convert the data object to a string.
      let stringData = JSON.stringify(valueObject);     

      // Load the readable stream with data.
      sourceStream.push(stringData + '\n');                  
    }       

    // Tell the stream no more data is coming.
    sourceStream.push(null);     

    callback(200, sourceStream, 'stream');             

  }); // End of: readInterface.on('close', function(){...}   

}; // End of: handlers._users.get = function(data, callback){do stuff}
...