2011-01-04 9 views
64

estoy curioseaba con Node.js y he descubierto dos maneras de leer un archivo y enviarlo por el cable, una vez que haya establecido que no existe y he enviado el tipo MIME adecuado con writeHead:¿Cuáles son los pros y los contras de fs.createReadStream vs fs.readFile en node.js?

// read the entire file into memory and then spit it out 

fs.readFile(filename, function(err, data){ 
    if (err) throw err; 
    response.write(data, 'utf8'); 
    response.end(); 
}); 

// read and pass the file as a stream of chunks 

fs.createReadStream(filename, { 
    'flags': 'r', 
    'encoding': 'binary', 
    'mode': 0666, 
    'bufferSize': 4 * 1024 
}).addListener("data", function(chunk) { 
    response.write(chunk, 'binary'); 
}).addListener("close",function() { 
    response.end(); 
}); 

¿Estoy en lo correcto al asumir que fs.createReadStream podría proporcionar una mejor experiencia de usuario si el archivo en cuestión era algo grande, como un video? Se siente como si fuera menos bloqueado; ¿Es esto cierto? ¿Hay otros pros, contras, advertencias o errores que necesito saber?

Respuesta

54

Un mejor enfoque, si sólo va a conectar "datos" a "escritura()" y "cerca" de "fin()":

// 0.3.x style 
fs.createReadStream(filename, { 
    'bufferSize': 4 * 1024 
}).pipe(response) 

// 0.2.x style 
sys.pump(fs.createReadStream(filename, { 
    'bufferSize': 4 * 1024 
}), response) 

El enfoque read.pipe(write) o sys.pump(read, write) tiene el beneficio de también agregar control de flujo. Por lo tanto, si la secuencia de escritura no puede aceptar datos tan rápido, le indicará a la secuencia de lectura que retroceda, a fin de minimizar la cantidad de datos almacenados en la memoria.

El flags:"r" y mode:0666 están implicados por el hecho de que es un FileReadStream. La codificación binary está en desuso: si no se especifica una codificación, simplemente funcionará con los almacenamientos intermedios de datos sin formato.

También, se podría añadir algunas otras cosas que harán que su archivo de servir a un conjunto mucho más pulido:

  1. Huela para req.headers.range y ver si coincide con una cadena como /bytes=([0-9]+)-([0-9]+)/. De ser así, solo desea transmitir desde esa ubicación de inicio a fin. (El número que falta significa 0 o "el final").
  2. Hash el inode y el tiempo de creación de la llamada stat() en un encabezado ETag. Si obtiene un encabezado de solicitud con "if-none-match" que coincide con ese encabezado, envíe de vuelta un 304 Not Modified.
  3. Compruebe el encabezado if-modified-since con la fecha mtime en el objeto stat. 304 si no fue modificado desde la fecha provista.

Además, en general, si puede, envíe un encabezado Content-Length. (Está estableciendo el archivo, por lo que debería tener esto.)

+0

¡Impresionante, gracias! –

+0

+1 Muy informativo. Gracias. – styfle

+0

@isaacs, ¿podría darnos un ejemplo de cómo se podrían implementar esos 3 pasos, gracias! –

32

fs.readFile cargará todo el archivo en la memoria como usted señaló, mientras que como fs.createReadStream leerá el archivo en trozos del tamaño que especifique.

El cliente también comenzará a recibir datos más rápido usando fs.createReadStream, ya que se envía en fragmentos mientras se lee, mientras que fs.readFile leerá el archivo completo y solo luego comenzará a enviarlo al cliente. Esto podría ser insignificante, pero puede marcar la diferencia si el archivo es muy grande y los discos son lentos.

Sin embargo, piense en esto, si ejecuta estas dos funciones en un archivo de 100MB, la primera usará 100MB de memoria para cargar el archivo mientras que este último solo usará como máximo 4KB.

Editar: Realmente no veo ningún motivo por el que usaría fs.readFile especialmente porque dijo que abrirá archivos de gran tamaño.

+0

Eso significa que con 'fs.readFile' no podemos coger el progreso por ejemplo? – Elemento0

0

Otra cosa, quizás no tan conocida, es que creo que el nodo es mejor para limpiar la memoria no utilizada después de usar fs.readFile en comparación con fs.createReadStream.Debe probar esto para verificar qué funciona mejor. Además, sé que con cada nueva versión de Node, esto ha mejorado (es decir, el recolector de basura se ha vuelto más inteligente con este tipo de situaciones).

1

Si se trata de un archivo grande, "readFile" acapararía la memoria ya que almacena todo el contenido del archivo en la memoria y puede colgar el sistema. Mientras ReadStream lee en trozos.

Ejecute este código y observe el uso de memoria en la pestaña de rendimiento del administrador de tareas.

var fs = require('fs'); 

const file = fs.createWriteStream('./big_file'); 


for(let i=0; i<= 1000000000; i++) { 
    file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n'); 
} 

file.end(); 


//.............. 
fs.readFile('./big_file', (err, data) => { 
    if (err) throw err; 
    console.log("done !!"); 
}); 

De hecho, no verá "hecho !!" mensaje. "readFile" no podría leer el contenido del archivo ya que el búfer no es lo suficientemente grande como para contener el contenido del archivo.

Ahora, en lugar de "readFile", use el flujo de lectura y el uso de la memoria del monitor.

Nota: El código se toma de curso Samer buna Nodo de Pluralsight

Cuestiones relacionadas