2012-04-28 11 views
37

मैं इंटरनेट से एक ज़िप फ़ाइल डाउनलोड करना चाहता हूं और इसे अस्थायी फ़ाइल में सहेजे बिना स्मृति में अनजिप करना चाहता हूं। मैं यह कैसे कर सकता हूँ?NodeJs में मेमोरी में ज़िप फ़ाइल को डाउनलोड और अनजिप कैसे करें?

यहाँ मैं क्या करने की कोशिश की है:

var url = 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip'; 

var request = require('request'), fs = require('fs'), zlib = require('zlib'); 

    request.get(url, function(err, res, file) { 
    if(err) throw err; 
    zlib.unzip(file, function(err, txt) { 
     if(err) throw err; 
     console.log(txt.toString()); //outputs nothing 
    }); 
    }); 

[संपादित करें] के रूप में, सुझाव, मैं ADM-जिप लाइब्रेरी का उपयोग करने की कोशिश की और मैं अभी भी यह काम नहीं कर सकते हैं:

var ZipEntry = require('adm-zip/zipEntry'); 
request.get(url, function(err, res, zipFile) { 
     if(err) throw err; 
     var zip = new ZipEntry(); 
     zip.setCompressedData(new Buffer(zipFile.toString('utf-8'))); 
     var text = zip.getData(); 
     console.log(text.toString()); // fails 
    }); 
+5

ध्यान दें कि 'zlib' ज़िप फ़ाइल प्रारूप को संभाल नहीं करता है, यह केवल gzip को नियंत्रित करता है और स्वरूपों को डिफ्लेट करता है। 'Zlib.unzip' फ़ंक्शन को भ्रामक रूप से नामित किया गया है क्योंकि यह केवल gzip को डीकंप्रेस करता है और स्वरूपों को डिफ्लेट करता है। आपको एक ज़िप प्रारूप पुस्तकालय की आवश्यकता है। –

+1

यह ज़िप्फाइल आशाजनक दिखता है https://github.com/springmeyer/node-zipfile/blob/master/README.md –

+0

@Dan: असल में, zlib भी ['zlib' प्रारूप] को नियंत्रित करता है (http: //www.ietf .org/rfc/rfc1950.txt) (जो बदले में डिफ्लेट का उपयोग करता है)। लेकिन यह यहां पूरी तरह से अप्रासंगिक है, इसलिए +1 :-) – Cameron

उत्तर

54
  • आपको एक पुस्तकालय की आवश्यकता है जो बफर को संभाल सकता है। adm-zip के नवीनतम संस्करण को करना होगा:

    npm install git://github.com/cthackers/adm-zip.git

  • मेरे समाधान http.get विधि का उपयोग करता है, क्योंकि यह बफर हिस्सा देता है।

कोड:

var file_url = 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip'; 

var request = require('request'); 
var fs = require('fs'); 
var AdmZip = require('adm-zip'); 
var http = require('http'); 
var url = require('url'); 

var options = { 
    host: url.parse(file_url).host, 
    port: 80, 
    path: url.parse(file_url).pathname 
}; 

http.get(options, function(res) { 
    var data = [], dataLen = 0; 

    res.on('data', function(chunk) { 

      data.push(chunk); 
      dataLen += chunk.length; 

     }).on('end', function() { 
      var buf = new Buffer(dataLen); 

      for (var i=0, len = data.length, pos = 0; i < len; i++) { 
       data[i].copy(buf, pos); 
       pos += data[i].length; 
      } 

      var zip = new AdmZip(buf); 
      var zipEntries = zip.getEntries(); 
      console.log(zipEntries.length) 

      for (var i = 0; i < zipEntries.length; i++) 
       console.log(zip.readAsText(zipEntries[i])); 
     }); 
}); 

विचार बफ़र्स की एक सरणी बनाने और उन्हें अंत में एक नया एक में जोड़ रहा है। यह इस तथ्य के कारण है कि बफर का आकार बदला नहीं जा सकता है।

+1

इस अगली बार उपयोग करने का प्रयास करें: https://github.com/kilianc/node-bufferjoiner :) – kilianc

+0

हाँ, धन्यवाद ... हमेशा की तरह, ऐसी कई चीजें हैं जिनका उपयोग किया जा सकता है। मैंने इसे माना, लेकिन सोचा कि सिर्फ node.js कोड के साथ एक उदाहरण बेहतर हो सकता है। – mihai

+2

मैं बस जोर देना चाहता हूं कि 'npm इंस्टॉल adm-zip' के साथ केवल एडम-ज़िप स्थापित करना काम नहीं करेगा क्योंकि केवल github पर नवीनतम संस्करण बफर का समर्थन करता है। – enyo

4

आप तो मैकोज़ या लिनक्स के तहत, आप कमांड का उपयोग stdin से अनजिप करने के लिए कर सकते हैं।

इस उदाहरण में मैं एक Buffer वस्तु में फाइल सिस्टम से ज़िप फ़ाइल पढ़ रहा हूँ, लेकिन यह भी एक डाउनलोड की गई फ़ाइल के साथ काम करता है:

// Get a Buffer with the zip content 
var fs = require("fs") 
    , zip = fs.readFileSync(__dirname + "/test.zip"); 


// Now the actual unzipping: 
var spawn = require('child_process').spawn 
    , fileToExtract = "test.js" 
    // -p tells unzip to extract to stdout 
    , unzip = spawn("unzip", ["-p", "/dev/stdin", fileToExtract ]) 
    ; 

// Write the Buffer to stdin 
unzip.stdin.write(zip); 

// Handle errors 
unzip.stderr.on('data', function (data) { 
    console.log("There has been an error: ", data.toString("utf-8")); 
}); 

// Handle the unzipped stdout 
unzip.stdout.on('data', function (data) { 
    console.log("Unzipped file: ", data.toString("utf-8")); 
}); 

unzip.stdin.end(); 

वास्तव में से केवल नोड संस्करण है कौन सा:

cat test.zip | unzip -p /dev/stdin test.js 

संपादित: यह ध्यान देने योग्य है कि इस करता है, तो इनपुट जिप भी stdin से एक हिस्सा पढ़ा जाए बड़ा है काम नहीं करेगा लायक है। आप बड़ा फ़ाइलों को पढ़ने की जरूरत है, और अपने ज़िप फ़ाइल केवल एक फ़ाइल है, तो आप unzip के बजाय funzip उपयोग कर सकते हैं:

var unzip = spawn("funzip"); 

अपने ज़िप फ़ाइल एकाधिक फ़ाइलें हैं (और फ़ाइल आप चाहते हैं पहले नहीं है, तो एक) मुझे यह कहने में डर है कि आप भाग्य से बाहर हैं। Unzip को .zip फ़ाइल में खोज करने की आवश्यकता है क्योंकि ज़िप फ़ाइलें केवल एक कंटेनर हैं, और अनजिप बस उसमें अंतिम फ़ाइल को अनजिप कर सकता है। उस स्थिति में आपको अस्थायी रूप से फ़ाइल को सहेजना होगा (node-temp आसान में आता है)।

+2

मुझे टिप्पणी छोड़ने के बिना मतदान करने के लिए किसी को तर्क देने में दिलचस्पी है। गंभीरता से, कारण क्या है इसके लिए काम नहीं करना है? मैं एक नौसिखिया हूं। – Strawberry

+0

मुझे कभी भी टिप्पणी के बिना डाउनवॉटिंग नहीं मिली है ... न ही मुझे लगता है क्योंकि यह केवल एक फ़ाइल के साथ काम करता है या यदि ज़िप काफी छोटा है। – enyo

+0

@Doug StackOverflow में आपका स्वागत है। मैं आपके उत्तर को ऊपर उठाता हूं। –

1

दो दिन पहले मॉड्यूल node-zip जारी किया गया है, जो ज़िप के केवल जावा संस्करण के लिए एक रैपर है: JSZip

var NodeZip = require('node-zip') 
    , zip = new NodeZip(zipBuffer.toString("base64"), { base64: true }) 
    , unzipped = zip.files["your-text-file.txt"].data; 
+0

पर पास कर सकते हैं नोड-ज़िप बफर का समर्थन नहीं करता है, इसलिए आपको एक स्ट्रिंग में कनवर्ट करने के लिए मजबूर होना पड़ता है, जो एक खराब बात है – Nikolai

5

दुर्भाग्य से आप कर सकते हैं नहीं पाइप unzip काम नोड के रूप में zlib lib आप आप कैश और प्रतिक्रिया के अंत इंतजार करना करने के लिए, की अनुमति देता है में प्रतिक्रिया स्ट्रीम। मेरा सुझाव है कि आप बड़ी फ़ाइलों के मामले में fs स्ट्रीम के जवाब को पाइप करने के लिए सुझाव दें, अन्यथा आप अपनी याददाश्त को एक झपकी में भर देंगे!

मैं पूरी तरह से समझ नहीं पा रहा हूं कि आप क्या करने की कोशिश कर रहे हैं, लेकिन imho यह सर्वोत्तम दृष्टिकोण है। आपको अपना डेटा केवल स्मृति में रखें जब आपको वास्तव में इसकी आवश्यकता हो,, और फिर csv parser पर स्ट्रीम करें।

आप अपने सभी डेटा स्मृति में आप from कि एक बफर के बजाय लेता है के साथ और GetData बदले में सीएसवी पार्सर विधि fromPath जगह ले सकता है सीधे unzipped

आप उपयोग कर सकते हैं रखना चाहते हैं AMDZip (के रूप में @mihai कहा) के बजाय node-zip, बस ध्यान क्योंकि AMDZip अभी तक NPM में प्रकाशित नहीं किया गया है भुगतान ताकि आप की जरूरत है:

$ npm install git://github.com/cthackers/adm-zip.git 

एनबी धारणा: ज़िप फ़ाइल में शामिल है केवल एक फ़ाइल

var request = require('request'), 
    fs = require('fs'), 
    csv = require('csv') 
    NodeZip = require('node-zip') 

function getData(tmpFolder, url, callback) { 
    var tempZipFilePath = tmpFolder + new Date().getTime() + Math.random() 
    var tempZipFileStream = fs.createWriteStream(tempZipFilePath) 
    request.get({ 
    url: url, 
    encoding: null 
    }).on('end', function() { 
    fs.readFile(tempZipFilePath, 'base64', function (err, zipContent) { 
     var zip = new NodeZip(zipContent, { base64: true }) 
     Object.keys(zip.files).forEach(function (filename) { 
     var tempFilePath = tmpFolder + new Date().getTime() + Math.random() 
     var unzipped = zip.files[filename].data 
     fs.writeFile(tempFilePath, unzipped, function (err) { 
      callback(err, tempFilePath) 
     }) 
     }) 
    }) 
    }).pipe(tempZipFileStream) 
} 

getData('/tmp/', 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip', function (err, path) { 
    if (err) { 
    return console.error('error: %s' + err.message) 
    } 
    var metadata = [] 
    csv().fromPath(path, { 
    delimiter: '|', 
    columns: true 
    }).transform(function (data){ 
    // do things with your data 
    if (data.NAME[0] === '#') { 
     metadata.push(data.NAME) 
    } else { 
     return data 
    } 
    }).on('data', function (data, index) { 
    console.log('#%d %s', index, JSON.stringify(data, null, ' ')) 
    }).on('end',function (count) { 
    console.log('Metadata: %s', JSON.stringify(metadata, null, ' ')) 
    console.log('Number of lines: %d', count) 
    }).on('error', function (error) { 
    console.error('csv parsing error: %s', error.message) 
    }) 
}) 
-2

वर FS = ('एफएस) की आवश्यकता होती है; var unzip = आवश्यकता ('unzip');

('पथ/a.zip ./') वर्तमान शब्दकोश

fs.createReadStream को // unzip a.zip पाइप (unzip.Extract ({पथ: './path/'}))। ;

मैंने अनजिप मॉड्यूल का उपयोग किया, और यह काम किया।

+1

प्रश्न "निर्दिष्ट एक अस्थायी फ़ाइल में सहेजे बिना " –