2012-02-28 38 views
18

साथ बड़ी फ़ाइलों को मैं का उपयोग कर Node.js साथ एक बड़ी फ़ाइल लिख रहा हूँ एक writable stream:लेखन Node.js

var fs  = require('fs'); 
var stream = fs.createWriteStream('someFile.txt', { flags : 'w' }); 

var lines; 
while (lines = getLines()) { 
    for (var i = 0; i < lines.length; i++) { 
     stream.write(lines[i]); 
    } 
} 

मैं अगर इस योजना drain घटना का उपयोग किए बिना सुरक्षित है सोच रहा हूँ? यदि यह नहीं है (जो मुझे लगता है कि मामला है), फ़ाइल में मनमाने ढंग से बड़े डेटा लिखने के लिए पैटर्न क्या है?

var fs = require('fs'); 
var stream = fs.createWriteStream('someFile.txt', {flags: 'w'}); 

var lines; 
while (lines = getLines()) { 
    for (var i = 0; i < lines.length; i++) { 
     stream.write(lines[i]); //<-- the place to test 
    } 
} 

जो आप नहीं कर रहे हैं:

उत्तर

13

इस तरह मैंने अंततः इसे किया। पीछे का विचार ReadStream इंटरफ़ेस को कार्यान्वित करने योग्य पठनीय स्ट्रीम बनाना है और फिर लिखने योग्य स्ट्रीम के लिए डेटा पाइप करने के लिए pipe() विधि का उपयोग करें।

var fs = require('fs'); 
var writeStream = fs.createWriteStream('someFile.txt', { flags : 'w' }); 
var readStream = new MyReadStream(); 

readStream.pipe(writeStream); 
writeStream.on('close', function() { 
    console.log('All done!'); 
}); 

MyReadStream वर्ग के उदाहरण नेवला QueryStream से लिया जा सकता है।

+12

आपको रीडस्ट्रीम() की आवश्यकता क्यों है जब हम केवल फाइल में चीजों को लिखने में रुचि रखते हैं? – krjampani

+0

@nab धन्यवाद। जब पाइपिंग लगता है कि यह लाइन फीड के लिए \ r \ n' नहीं जोड़ रहा है, तो प्रत्येक पंक्ति को एक से जोड़ दें ... – loretoparisi

9

नाली के पीछे विचार यह है कि आप यहाँ परीक्षण करने के लिए इसका इस्तेमाल करते हैं होता है। तो आपको इसे "पुनर्वित्त" बनाने के लिए पुनर्चक्रण करने की आवश्यकता होगी।

var fs = require('fs'); 
var stream = fs.createWriteStream('someFile.txt', {flags: 'w'}); 

var lines; 
while (lines = getLines()) { 
    for (var i = 0; i < lines.length; i++) { 
     var written = stream.write(lines[i]); //<-- the place to test 
     if (!written){ 
      //do something here to wait till you can safely write again 
      //this means prepare a buffer and wait till you can come back to finish 
      // lines[i] -> remainder 
     } 
    } 
} 

हालांकि, क्या इसका मतलब यह है कि आपको प्रतीक्षा करते समय भी बफरिंग गेटलाइन रखने की आवश्यकता है?

var fs = require('fs'); 
var stream = fs.createWriteStream('someFile.txt', {flags: 'w'}); 

var lines, 
    buffer = { 
    remainingLines = [] 
    }; 
while (lines = getLines()) { 
    for (var i = 0; i < lines.length; i++) { 
     var written = stream.write(lines[i]); //<-- the place to test 
     if (!written){ 
      //do something here to wait till you can safely write again 
      //this means prepare a buffer and wait till you can come back to finish 
      // lines[i] -> remainder 
      buffer.remainingLines = lines.slice(i); 
      break; 
      //notice there's no way to re-run this once we leave here. 
     } 
    } 
} 

stream.on('drain',function(){ 
    if (buffer.remainingLines.length){ 
    for (var i = 0; i < buffer.remainingLines.length; i++) { 
     var written = stream.write(buffer.remainingLines[i]); //<-- the place to test 
     if (!written){ 
     //do something here to wait till you can safely write again 
     //this means prepare a buffer and wait till you can come back to finish 
     // lines[i] -> remainder 
     buffer.remainingLines = lines.slice(i); 
     } 
    } 
    } 
}); 
+3

अपने स्वयं के बफर का उपयोग करना अनावश्यक है। Node.js आपके लिए किया है। स्रोत फ़ाइल नोडजेस-स्रोत/lib/fs.js # WriteStream.prototype.write – ayanamist

2

[संपादित करें] अद्यतन Node.js writable.write(...) API docs कहते हैं:

[यह] वापसी मान सख्ती से सलाहकार है। आप लिखना जारी रख सकते हैं, भले ही यह झूठी हो। हालांकि, लिखने में स्मृति में बफर किया जाएगा, इसलिए यह अत्यधिक नहीं करना सबसे अच्छा है। इसके बजाय, अधिक डेटा लिखने से पहले नाली घटना की प्रतीक्षा करें।

[मूल रूप]stream.write(...) documentation (जोर मेरा) से:

रिटर्न true स्ट्रिंग कर्नेल बफर के लिए प्लावित किया गया है। यह संकेत करने के लिए false देता है कि कर्नेल बफर भरा हुआ है, और डेटा में भेजा जाएगा।

मैं व्याख्या इसका मतलब यह करने के लिए "लिखने" फ़ंक्शन कि true यदि दिए गए स्ट्रिंग तुरंत अंतर्निहित ओएस बफर या false लिए लिखा गया था अगर यह अभी तक नहीं लिखा गया था, लेकिन लिखने समारोह (द्वारा लिखा जाएगा उदाहरण के लिए लिखितस्ट्रीम द्वारा संभवतः आपके लिए buffered किया गया था) ताकि आपको फिर से "लिखने" को कॉल करने की आवश्यकता न हो।

+1

पढ़ें, लेकिन "इस तरीके से फ़ाइल डिस्क्रिप्टर लिखते समय, स्ट्रीम से पहले डिस्क्रिप्टर को बंद करने से जोखिम अमान्य (बंद) एफडी भेजता है।" मुझे लगता है कि बफर पूर्ण होने का मतलब है कि यह आपके द्वारा किसी और कोड को स्वीकार नहीं कर सकता है। मैं ईमानदारी से नहीं जानता, और केवल इसे एक उत्तर के रूप में अपना सर्वश्रेष्ठ अनुमान दिया। – jcolebrand

+0

@jcolebrand: हाँ, मुझे या तो पता नहीं है, लेकिन मुझे लगता है कि "नाली" घटना का संकेत है कि ओएस तुरंत लिखने के लिए तैयार है, अगर आप वास्तव में किसी भी तरह के बफरिंग से बचना चाहते हैं, तो यह स्वयं या WriteStream "लिखें" विधि से। हालांकि, "नाली" के लिए दस्तावेज़ "* फिर से लिखने के लिए सुरक्षित *" का उल्लेख करते हैं, जो या तो मेरी व्याख्या के खिलाफ शब्द या सबूत की खराब पसंद है! – maerics

+0

डेटा 404 के लिंक। – Alan

2

मुझे बड़ी फाइलों से निपटने के लिए स्ट्रीम खराब प्रदर्शन करने का तरीका मिला - ऐसा इसलिए है क्योंकि आप पर्याप्त इनपुट बफर आकार निर्धारित नहीं कर सकते हैं (कम से कम मुझे ऐसा करने का एक अच्छा तरीका पता नहीं है)। यह मैं क्या कर रहा है:

var fs = require('fs'); 

var i = fs.openSync('input.txt', 'r'); 
var o = fs.openSync('output.txt', 'w'); 

var buf = new Buffer(1024 * 1024), len, prev = ''; 

while(len = fs.readSync(i, buf, 0, buf.length)) { 

    var a = (prev + buf.toString('ascii', 0, len)).split('\n'); 
    prev = len === buf.length ? '\n' + a.splice(a.length - 1)[0] : ''; 

    var out = ''; 
    a.forEach(function(line) { 

     if(!line) 
      return; 

     // do something with your line here 

     out += line + '\n'; 
    }); 

    var bout = new Buffer(out, 'ascii'); 
    fs.writeSync(o, bout, 0, bout.length); 
} 

fs.closeSync(o); 
fs.closeSync(i); 
+0

क्या आपके पास 'readStream/writeStream' और' readSync/writeSync' परीक्षण के बीच कोई बेंचमार्क है इसकी पुष्टि करने के लिए का जवाब? धन्यवाद। – loretoparisi

1

इस संभाल करने के लिए साफ रास्ता बनाने के लिए अपनी लाइन जनरेटर एक readable stream है - चलो यह lineReader कहते हैं।उसके बाद निम्न स्वचालित रूप से बफ़र्स और आप के लिए अच्छी तरह से समाप्त हो रही संभाल होगा:

lineReader.pipe(fs.createWriteStream('someFile.txt')); 

आप एक पठनीय धारा बनाने के लिए नहीं करना चाहते हैं, तो आप बफर-परिपूर्णता के लिए write के उत्पादन को सुनने और इस तरह से प्रतिक्रिया कर सकते हैं:

var i = 0, n = lines.length; 
function write() { 
    if (i === n) return; // A callback could go here to know when it's done. 
    while (stream.write(lines[i++]) && i < n); 
    stream.once('drain', write); 
} 
write(); // Initial call. 

इस स्थिति का एक लंबा उदाहरण here पाया जा सकता है।

1

इस प्रश्न के कई सुझाए गए उत्तरों ने पूरी तरह से धाराओं के बारे में बिंदु को याद किया है।

इस मॉड्यूल https://www.npmjs.org/package/JSONStream

मदद कर सकते हैं हालांकि, की सुविधा देता है के रूप में वर्णित स्थिति लगता है और कोड अपने आप को लिखें। आप एक मोंगोडीबी से स्ट्रीम के रूप में पढ़ रहे हैं, ऑब्जेक्टमोड = डिफ़ॉल्ट रूप से सत्य के साथ।

यदि आप फ़ाइल को सीधे स्ट्रीम करने का प्रयास करते हैं तो यह समस्याएं उत्पन्न करेगा - "अवैध गैर-स्ट्रिंग/बफर खंड" त्रुटि जैसी कुछ।

इस प्रकार की समस्या का समाधान बहुत आसान है।

बस एक स्ट्रिंग लिखने योग्य ऑब्जेक्ट को पठनीय ऑब्जेक्ट को अनुकूलित करने के लिए पठनीय और लिखने योग्य के बीच एक और ट्रांसफॉर्म डालें।

नमूना कोड समाधान:

var fs = require('fs'), 
    writeStream = fs.createWriteStream('./out' + process.pid, {flags: 'w', encoding: 'utf-8' }), 
    stream = require('stream'), 
    stringifier = new stream.Transform(); 
stringifier._writableState.objectMode = true; 
stringifier._transform = function (data, encoding, done) { 
    this.push(JSON.stringify(data)); 
    this.push('\n'); 
    done(); 
} 
rowFeedDao.getRowFeedsStream(merchantId, jobId) 
.pipe(stringifier) 
.pipe(writeStream).on('error', function (err) { 
    // handle error condition 
} 
0

आप एक इनपुट स्ट्रीम के लिए ऐसा नहीं है, तो आप आसानी से पाइप का उपयोग नहीं कर सकते हैं। उपरोक्त में से कोई भी मेरे लिए काम नहीं करता है, नाली की घटना आग नहीं होती है। निम्नानुसार हल किया गया (टाइल्स उत्तर के आधार पर):

var lines[]; // some very large array 
var i = 0; 

function write() { 
    if (i < lines.length) { 
     wstream.write(lines[i]), function(err){ 
      if (err) { 
       console.log(err); 
      } else { 
       i++; 
       write(); 
      } 
     }); 
    } else { 
     wstream.end(); 
     console.log("done"); 
    } 
}; 
write();