मैं एक स्ट्रिंग चर में Node.js स्ट्रीम की सामग्री कैसे पढ़ सकता हूं?


113

मैं एक नोड प्रोग्राम पर हैक कर रहा हूं जो smtp-protocolएसएमटीपी ईमेल को पकड़ने और मेल डेटा पर कार्य करने के लिए उपयोग करता है। पुस्तकालय एक स्ट्रीम के रूप में मेल डेटा प्रदान करता है, और मुझे नहीं पता कि इसे एक स्ट्रिंग में कैसे लाया जाए।

मैं वर्तमान में इसे स्टडआउट करने के लिए लिख रहा हूं stream.pipe(process.stdout, { end: false }), लेकिन जैसा कि मैंने कहा, मुझे इसके बजाय एक स्ट्रिंग में स्ट्रीम डेटा की आवश्यकता है, जिसका उपयोग मैं एक बार स्ट्रीम समाप्त होने के बाद कर सकता हूं।

मैं एक Node.js स्ट्रीम से सभी डेटा को एक स्ट्रिंग में कैसे एकत्रित करूं?


आपको स्ट्रीम को कॉपी करना चाहिए या इसे ध्वजांकित करना चाहिए (ऑटो क्लिक: असत्य)। याददाश्त को प्रदूषित करना बुरा व्यवहार है।
19

जवाबों:


41

(यह उत्तर वर्षों पहले का है, जब यह सबसे अच्छा उत्तर था। अब इसके नीचे एक बेहतर उत्तर है। मैंने नोड.जेएस के साथ नहीं रखा है, और मैं इस उत्तर को हटा नहीं सकता क्योंकि यह इस प्रश्न पर सही है। "। यदि आप नीचे क्लिक करने की सोच रहे हैं, तो आप मुझे क्या करना चाहते हैं?"

कुंजी पढ़ने योग्य स्ट्रीम की घटनाओं dataऔर endघटनाओं का उपयोग करना है । इन घटनाओं को सुनें:

stream.on('data', (chunk) => { ... });
stream.on('end', () => { ... });

जब आप प्राप्त करते हैं data ईवेंट , तो डेटा एकत्र करने के लिए बनाए गए बफ़र में डेटा का नया हिस्सा जोड़ें।

जब आप endईवेंट प्राप्त करते हैं , तो आवश्यक होने पर पूर्ण बफ़र को स्ट्रिंग में परिवर्तित करें। फिर आपको इसके साथ क्या करना है।


149
उत्तर को दर्शाने वाली कोड की कुछ पंक्तियाँ केवल एपीआई पर एक लिंक को इंगित करने के लिए बेहतर है। उत्तर से असहमत न हों, बस विश्वास न करें कि यह पर्याप्त है।
आर्केल्डन

3
नए नोड.जेएस संस्करणों के साथ, यह क्लीनर है: stackoverflow.com/a/35530615/271961
साइमन ए। युगस्टर

प्रॉमिस लाइब्रेरी के उपयोग की अनुशंसा नहीं करने के लिए उत्तर को अपडेट किया जाना चाहिए, लेकिन देशी प्रॉमिस का उपयोग करें।
डैन डस्केल्सस्कु

@DanDascalescu मैं आपसे सहमत हूं। समस्या यह है कि मैंने 7 साल पहले यह उत्तर लिखा था, और मैंने नोड.जेएस के साथ नहीं रखा है। यदि आप किसी और के लिए इसे अपडेट करना चाहते हैं, तो यह बहुत अच्छा होगा। या मैं बस इसे हटा सकता हूं, क्योंकि पहले से ही यह एक बेहतर जवाब है। आप क्या सुझाव देंगे?
कंट्रोलएटेलडेल

@ControlAltDel: मैं एक उत्तर को हटाने के लिए आपकी पहल की सराहना करता हूं जो अब सबसे अच्छा नहीं है। काश दूसरों में भी ऐसा ही अनुशासन होता
डेन डैस्कलेस्कु

129

एक और तरीका यह होगा कि स्ट्रीम को एक वादा (नीचे दिए गए उदाहरण को देखें) और एक चर में हल किए गए मान को असाइन करने के लिए then(या await) का उपयोग करें ।

function streamToString (stream) {
  const chunks = []
  return new Promise((resolve, reject) => {
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('error', reject)
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
  })
}

const result = await streamToString(stream)

मैं वास्तव में धाराओं और वादों के लिए नया हूँ और मुझे यह त्रुटि मिल रही है SyntaxError: await is only valid in async function:। मैं क्या गलत कर रहा हूं?
जॉन जूल 26'19

आपको एक async फ़ंक्शन के भीतर स्ट्रीमटॉस्ट्रिंग फ़ंक्शन को कॉल करना होगा। इससे बचने के लिए आप यह भी कर सकते हैंstreamToString(stream).then(function(response){//Do whatever you want with response});
एनक्लो क्रिएशन्स

23
यह शीर्ष उत्तर होना चाहिए। एकमात्र समाधान का उत्पादन करने के लिए बधाई, जो सब कुछ सही हो जाता है, (1) बंकरों के रूप में चोंच का भंडारण करना और केवल .toString("utf8")अंत में कॉल करना, डिकोड विफलता की समस्या से बचने के लिए यदि एक चिन मल्टीबाइट चरित्र के बीच में विभाजित है; (2) वास्तविक त्रुटि से निपटने; (3) किसी फ़ंक्शन में कोड डालना, इसलिए इसका पुन: उपयोग किया जा सकता है, कॉपी-पेस्ट नहीं किया गया; (4) प्रोमिस का उपयोग करना ताकि फ़ंक्शन को await-ed किया जा सके ; (५) छोटे कोड जो एक निश्चित एनपीएम पुस्तकालयों के विपरीत, एक लाख निर्भरता में नहीं खींचते हैं; (6) ईएस 6 वाक्यविन्यास और आधुनिक सर्वोत्तम प्रथाएं।
MultiplyByZer0

क्यों वादे में हिस्सा नहीं है?
जेनी ओ'रेली

1
चूंकि मैं संकेत के रूप में वर्तमान शीर्ष उत्तर का उपयोग करते हुए अनिवार्य रूप से समान कोड के साथ आया था, मैंने देखा है कि Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received type stringयदि धारा stringइसके बजाय विखंडन पैदा करती है तो उपरोक्त कोड विफल हो सकता है Buffer। का उपयोग chunks.push(Buffer.from(chunk))करना दोनों stringऔर Bufferचूजों के साथ काम करना चाहिए ।
आंद्रेई ने

67

उपरोक्त में से किसी ने भी मेरे लिए काम नहीं किया। मुझे बफ़र ऑब्जेक्ट का उपयोग करने की आवश्यकता है:

  const chunks = [];

  readStream.on("data", function (chunk) {
    chunks.push(chunk);
  });

  // Send the buffer or you can put it into a var
  readStream.on("end", function () {
    res.send(Buffer.concat(chunks));
  });

7
यह वास्तव में इसे करने का सबसे साफ तरीका है;)
इवो

7
बहुत अच्छा काम करता है। बस एक नोट: यदि आप एक उचित स्ट्रिंग प्रकार चाहते हैं, तो आपको कॉनसैट () कॉल के परिणामस्वरूप बफ़र ऑब्जेक्ट पर .toString () कॉल करने की आवश्यकता होगी
ब्रायन जॉनसन

64

आशा है कि यह उपरोक्त उत्तर से अधिक उपयोगी है:

var string = '';
stream.on('data',function(data){
  string += data.toString();
  console.log('stream data ' + part);
});

stream.on('end',function(){
  console.log('final output ' + string);
});

ध्यान दें कि स्ट्रिंग भागों को इकट्ठा करने के लिए स्ट्रिंग संयोजन सबसे प्रभावी तरीका नहीं है, लेकिन इसका उपयोग सादगी के लिए किया जाता है (और शायद आपका कोड दक्षता की परवाह नहीं करता है)।

इसके अलावा, यह कोड गैर-एएससीआईआई पाठ के लिए अप्रत्याशित विफलताओं का उत्पादन कर सकता है (यह मानता है कि हर चरित्र एक बाइट में फिट बैठता है), लेकिन शायद आप इस बारे में परवाह नहीं करते हैं, या तो।


4
स्ट्रिंग भागों को इकट्ठा करने का एक अधिक कुशल तरीका क्या होगा? स्व
sean2078

2
आप एक बफर डॉक्स.नोडजित्सु / आर्टिकल्स / एडवांस / बफर्स ​​/ इतवार- फ्यूज- बफर्स ​​का उपयोग कर सकते हैं लेकिन यह वास्तव में आपके उपयोग पर निर्भर करता है।
टॉम करचरे

2
स्ट्रिंग्स के एक सरणी का उपयोग करें जहां आप प्रत्येक नए चंक को सरणी में जोड़ते हैं और join("")अंत में सरणी पर कॉल करते हैं।
वलेरीउ पालोस

14
यह सही नहीं है। यदि बफ़र मल्टी-बाइट कोड पॉइंट से आधा होता है, तो स्ट्रस्ट्रिंग () विकृत विकृत -8 प्राप्त करेगा और आप अपनी स्ट्रिंग में का एक गुच्छा के साथ समाप्त करेंगे।
एलेक्सीगार्डन

2
@alextgordon सही है। कुछ बहुत ही दुर्लभ मामलों में जब मेरे पास बहुत सारे विखंडू होते थे, तो मुझे विराम मिलता था और विखंडन के अंत में। खासकर जब वहाँ जहां रूसी किनारों पर प्रतीक। तो यह विखंडू को सही करने और विखंडन को बदलने के बजाय अंत में उन्हें बदलने और उन्हें समाप्‍त करने के लिए सही है। मेरे मामले में अनुरोध एक सेवा से दूसरे में अनुरोध के साथ किया गया था। डिफ़ॉल्ट एन्कोडिंग के साथ
जेएस

21

मैं आम तौर पर एक स्ट्रिंग में एक धारा को बदलने के लिए इस सरल फ़ंक्शन का उपयोग कर रहा हूं:

function streamToString(stream, cb) {
  const chunks = [];
  stream.on('data', (chunk) => {
    chunks.push(chunk.toString());
  });
  stream.on('end', () => {
    cb(chunks.join(''));
  });
}

उपयोग उदाहरण:

let stream = fs.createReadStream('./myFile.foo');
streamToString(stream, (data) => {
  console.log(data);  // data is now my string variable
});

1
उपयोगी उत्तर लेकिन ऐसा लगता है कि प्रत्येक चंक को स्ट्रिंग में धकेलने से पहले उसे एक स्ट्रिंग में chunks.push(chunk.toString());
बदलना चाहिए

1
यह केवल एक ही है जो मेरे लिए काम करता है! बहुत बहुत धन्यवाद
538ROMEO

1
यह एक महान जवाब था!
आफ्टरट्रेल

12

और फिर भी वादों का उपयोग करते हुए तार के लिए एक और एक:

function getStream(stream) {
  return new Promise(resolve => {
    const chunks = [];

    # Buffer.from is required if chunk is a String, see comments
    stream.on("data", chunk => chunks.push(Buffer.from(chunk)));
    stream.on("end", () => resolve(Buffer.concat(chunks).toString()));
  });
}

उपयोग:

const stream = fs.createReadStream(__filename);
getStream(stream).then(r=>console.log(r));

.toString()यदि आवश्यक हो तो बाइनरी डेटा के साथ उपयोग करने के लिए निकालें ।

अद्यतन : @AndreiLED ने सही ढंग से बताया कि इसमें तार की समस्या है। मेरे पास नोड के संस्करण के साथ स्ट्रिंग्स वापस करने वाली स्ट्रीम नहीं मिल सकी, लेकिन एपीआई नोट यह संभव है।


मैंने देखा है कि Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received type stringअगर ऊपर के stringबजाय विखंडू पैदा करता है तो उपरोक्त कोड विफल हो सकता है Buffer। का उपयोग chunks.push(Buffer.from(chunk))करना दोनों stringऔर Bufferचूजों के साथ काम करना चाहिए ।
आंद्रेई ने

अच्छी बात है, मैंने जवाब अपडेट कर दिया है। धन्यवाद।
इस्टानी

8

नोडज के दस्तावेज़ीकरण से आपको यह करना चाहिए - हमेशा एक स्ट्रिंग को याद रखें बिना एन्कोडिंग के बिना एक बाइट्स का एक गुच्छा होता है:

var readable = getReadableStreamSomehow();
readable.setEncoding('utf8');
readable.on('data', function(chunk) {
  assert.equal(typeof chunk, 'string');
  console.log('got %d characters of string data', chunk.length);
})

6

धाराओं में एक साधारण .toString()कार्य नहीं होता है (जिसे मैं समझता हूं) और न ही ऐसा कुछ है.toStringAsync(cb) फ़ंक्शन की (जो मुझे समझ में नहीं आता है)।

इसलिए मैंने अपना स्वयं का सहायक कार्य बनाया:

var streamToString = function(stream, callback) {
  var str = '';
  stream.on('data', function(chunk) {
    str += chunk;
  });
  stream.on('end', function() {
    callback(str);
  });
}

// how to use:
streamToString(myStream, function(myStr) {
  console.log(myStr);
});

4

मेरे पास इस तरह का उपयोग करने के लिए अधिक भाग्य था:

let string = '';
readstream
    .on('data', (buf) => string += buf.toString())
    .on('end', () => console.log(string));

मैं नोड का उपयोग करता हूं v9.11.1और कॉलबैक readstreamसे प्रतिक्रिया करता हूं http.get


3

सबसे साफ समाधान "स्ट्रिंग-स्ट्रीम" पैकेज का उपयोग करना हो सकता है, जो एक वादा के साथ एक धारा को स्ट्रिंग में परिवर्तित करता है।

const streamString = require('stream-string')

streamString(myStream).then(string_variable => {
    // myStream was converted to a string, and that string is stored in string_variable
    console.log(string_variable)

}).catch(err => {
     // myStream emitted an error event (err), so the promise from stream-string was rejected
    throw err
})

3

लोकप्रिय के साथ आसान तरीका (5 मी साप्ताहिक डाउनलोड) और हल्के हो जाओ-स्ट्रीम पुस्तकालय:

https://www.npmjs.com/package/get-stream

const fs = require('fs');
const getStream = require('get-stream');

(async () => {
    const stream = fs.createReadStream('unicorn.txt');
    console.log(await getStream(stream)); //output is string
})();

2

एक धारा reducer की तरह कुछ के बारे में क्या?

यहां ईएस 6 कक्षाओं का उपयोग करके एक उदाहरण दिया गया है कि एक का उपयोग कैसे करें।

var stream = require('stream')

class StreamReducer extends stream.Writable {
  constructor(chunkReducer, initialvalue, cb) {
    super();
    this.reducer = chunkReducer;
    this.accumulator = initialvalue;
    this.cb = cb;
  }
  _write(chunk, enc, next) {
    this.accumulator = this.reducer(this.accumulator, chunk);
    next();
  }
  end() {
    this.cb(null, this.accumulator)
  }
}

// just a test stream
class EmitterStream extends stream.Readable {
  constructor(chunks) {
    super();
    this.chunks = chunks;
  }
  _read() {
    this.chunks.forEach(function (chunk) { 
        this.push(chunk);
    }.bind(this));
    this.push(null);
  }
}

// just transform the strings into buffer as we would get from fs stream or http request stream
(new EmitterStream(
  ["hello ", "world !"]
  .map(function(str) {
     return Buffer.from(str, 'utf8');
  })
)).pipe(new StreamReducer(
  function (acc, v) {
    acc.push(v);
    return acc;
  },
  [],
  function(err, chunks) {
    console.log(Buffer.concat(chunks).toString('utf8'));
  })
);

1

यह मेरे लिए काम करता है और यह नोड v6.7.0 डॉक्स पर आधारित है :

let output = '';
stream.on('readable', function() {
    let read = stream.read();
    if (read !== null) {
        // New stream data is available
        output += read.toString();
    } else {
        // Stream is now finished when read is null.
        // You can callback here e.g.:
        callback(null, output);
    }
});

stream.on('error', function(err) {
  callback(err, null);
})

1

setEncoding ( 'UTF8');

ऊपर से सेबस्टियन जे।

मेरे पास परीक्षण कोड की कुछ पंक्तियों के साथ "बफर समस्या" थी, और एन्कोडिंग जानकारी को जोड़ा और इसे हल किया, नीचे देखें।

समस्या का प्रदर्शन

सॉफ्टवेयर

// process.stdin.setEncoding('utf8');
process.stdin.on('data', (data) => {
    console.log(typeof(data), data);
});

इनपुट

hello world

उत्पादन

object <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 0d 0a>

समाधान का प्रदर्शन

सॉफ्टवेयर

process.stdin.setEncoding('utf8'); // <- Activate!
process.stdin.on('data', (data) => {
    console.log(typeof(data), data);
});

इनपुट

hello world

उत्पादन

string hello world

1

सूचीबद्ध सभी उत्तर पठनीय मोड में पठनीय स्ट्रीम को खोलने के लिए दिखाई देते हैं जो कि NodeJS में डिफ़ॉल्ट नहीं है और इसकी सीमाएँ हो सकती हैं क्योंकि इसमें बैकस्पेस की सहायता का अभाव है जो NodeJS रुके हुए पठनीय स्ट्रीम मोड में प्रदान करता है। यहाँ जस्ट बफ़र्स, नेटिव स्ट्रीम और नेटिव स्ट्रीम ट्रांसफ़ॉर्म और ऑब्जेक्ट मोड के लिए समर्थन का उपयोग करते हुए एक कार्यान्वयन है

import {Transform} from 'stream';

let buffer =null;    

function objectifyStream() {
    return new Transform({
        objectMode: true,
        transform: function(chunk, encoding, next) {

            if (!buffer) {
                buffer = Buffer.from([...chunk]);
            } else {
                buffer = Buffer.from([...buffer, ...chunk]);
            }
            next(null, buffer);
        }
    });
}

process.stdin.pipe(objectifyStream()).process.stdout

1

आप इस बारे में क्या सोचते हैं ?

// lets a ReadableStream under stream variable 
const chunks = [];

for await (let chunk of stream) {
    chunks.push(chunk)
}

const buffer  = Buffer.concat(chunks);
const str = buffer.toString("utf-8")

काम करता है, बहुत साफ, कोई निर्भरता, अच्छा!
वीरुस्तरीनी

0

काफी लोकप्रिय stream-buffersपैकेज का उपयोग करना जो आपके पास पहले से ही आपके प्रोजेक्ट निर्भरता में है, यह बहुत सीधा है:

// imports
const { WritableStreamBuffer } = require('stream-buffers');
const { promisify } = require('util');
const { createReadStream } = require('fs');
const pipeline = promisify(require('stream').pipeline);

// sample stream
let stream = createReadStream('/etc/hosts');

// pipeline the stream into a buffer, and print the contents when done
let buf = new WritableStreamBuffer();
pipeline(stream, buf).then(() => console.log(buf.getContents().toString()));

0

मेरे मामले में, सामग्री प्रकार प्रतिक्रिया शीर्ष लेख सामग्री-प्रकार: पाठ / सादा था । तो, मैंने बफ़र के डेटा को पढ़ा है जैसे:

let data = [];
stream.on('data', (chunk) => {
 console.log(Buffer.from(chunk).toString())
 data.push(Buffer.from(chunk).toString())
});
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.