2011-01-23 10 views
29

Estoy tratando de crear un programa de descarga de archivos como un servicio en segundo plano, pero cuando se programa un archivo grande, primero se guarda en la memoria y luego, al final de la descarga el archivo se escribe en el disco.Descargar archivo grande con node.js evitando el alto consumo de memoria

¿Cómo puedo hacer que el archivo se escriba gradualmente en el disco conservando la memoria teniendo en cuenta que puedo tener muchos archivos que se descargan al mismo tiempo?

Aquí está el código de `m usando:

var sys = require("sys"), 
    http = require("http"), 
    url = require("url"), 
    path = require("path"), 
    fs = require("fs"), 
    events = require("events"); 

var downloadfile = "http://nodejs.org/dist/node-v0.2.6.tar.gz"; 

var host = url.parse(downloadfile).hostname 
var filename = url.parse(downloadfile).pathname.split("/").pop() 

var theurl = http.createClient(80, host); 
var requestUrl = downloadfile; 
sys.puts("Downloading file: " + filename); 
sys.puts("Before download request"); 
var request = theurl.request('GET', requestUrl, {"host": host}); 
request.end(); 

var dlprogress = 0; 


setInterval(function() { 
    sys.puts("Download progress: " + dlprogress + " bytes"); 
}, 1000); 


request.addListener('response', function (response) { 
    response.setEncoding('binary') 
    sys.puts("File size: " + response.headers['content-length'] + " bytes.") 
    var body = ''; 
    response.addListener('data', function (chunk) { 
     dlprogress += chunk.length; 
     body += chunk; 
    }); 
    response.addListener("end", function() { 
     fs.writeFileSync(filename, body, 'binary'); 
     sys.puts("After download finished"); 
    }); 

}); 
+0

alguna posibilidad de que podría compartir el resultado final ? Estoy buscando algo como esto ... – Eli

+0

Intenté implementar una característica para seguir los redireccionamientos 302 pero no creo que esté funcionando correctamente. Tal vez podrías intentarlo. Ahí está: https://gist.github.com/1297063 – Carlosedp

Respuesta

26

me cambió la devolución de llamada a:

request.addListener('response', function (response) { 
     var downloadfile = fs.createWriteStream(filename, {'flags': 'a'}); 
     sys.puts("File size " + filename + ": " + response.headers['content-length'] + " bytes."); 
     response.addListener('data', function (chunk) { 
      dlprogress += chunk.length; 
      downloadfile.write(chunk, encoding='binary'); 
     }); 
     response.addListener("end", function() { 
      downloadfile.end(); 
      sys.puts("Finished downloading " + filename); 
     }); 

    }); 

Esto funcionó a la perfección.

+0

¿No se supone que preferimos setEncoding (null) en lugar de 'binary'? –

+0

'{'flags': 'a'}' agregará los datos al archivo si ya existe – respectTheCode

1

En lugar de mantener el contenido en la memoria en el oyente "data" caso se debe escribir en el fichero en modo append.

2

Al descargar archivos de gran tamaño, utilice fs.write y no writeFile ya que anulará el contenido anterior.

function downloadfile(res) { 
    var requestserver = http.request(options, function(r) { 
     console.log('STATUS: ' + r.statusCode); 
     console.log('HEADERS: ' + JSON.stringify(r.headers)); 

     var fd = fs.openSync('sai.tar.gz', 'w'); 

     r.on('data', function (chunk) { 
      size += chunk.length; 
      console.log(size+'bytes received'); 
      sendstatus(res,size); 
      fs.write(fd, chunk, 0, chunk.length, null, function(er, written) { 
      }); 
     }); 
     r.on('end',function(){ 
      console.log('\nended from server'); 
      fs.closeSync(fd); 
      sendendstatus(res); 
     }); 
    }); 
} 
+1

fs.write no es seguro si no espera la devolución de llamada. Deberías usar un WriteStream. – respectTheCode

+0

Mucho mejor simplemente conectar 'res' a la secuencia de archivos de escritura. – Brad

2

¿El paquete de solicitud funciona para sus usos?

Te permite hacer cosas como esta:

request(downloadurl).pipe(fs.createWriteStream(downloadtohere)) 
+0

Ni siquiera necesita una solicitud para esto. Simplemente pipe 'res' from' http.get' o lo que sea que se esté usando. – Brad

1

Use corrientes como Carter Cole sugirieron. Aquí está un ejemplo más completo

var inspect = require('eyespect').inspector(); 
var request = require('request'); 
var filed = require('filed'); 
var temp = require('temp'); 
var downloadURL = 'http://upload.wikimedia.org/wikipedia/commons/e/ec/Hazard_Creek_Kayaker.JPG'; 
var downloadPath = temp.path({prefix: 'singlePageRaw', suffix: '.jpg'}); 

var downloadFile = filed(downloadPath); 
var r = request(downloadURL).pipe(downloadFile); 


r.on('data', function(data) { 
    inspect('binary data received'); 
}); 
downloadFile.on('end', function() { 
    inspect(downloadPath, 'file downloaded to path'); 
}); 

downloadFile.on('error', function (err) { 
    inspect(err, 'error downloading file'); 
}); 

Es posible que tenga que instalar módulos que se pueden hacer a través de npm install filed request eyespect temp

+1

No hay ninguna razón para usar los ojos, archivados o temporales. El ejemplo es bueno, pero parece hinchado. –

4

Tome un vistazo a http-request:

// shorthand syntax, buffered response 
http.get('http://localhost/get', function (err, res) { 
    if (err) throw err; 
    console.log(res.code, res.headers, res.buffer.toString()); 
}); 

// save the response to 'myfile.bin' with a progress callback 
http.get({ 
    url: 'http://localhost/get', 
    progress: function (current, total) { 
     console.log('downloaded %d bytes from %d', current, total); 
    } 
}, 'myfile.bin', function (err, res) { 
    if (err) throw err; 
    console.log(res.code, res.headers, res.file); 
}); 
Cuestiones relacionadas