1. Análisis de apertura
Stream es una interfaz abstracta implementada por muchos objetos en el nodo. Por ejemplo, una solicitud a un servidor HTTP es una transmisión, y StDout también es una transmisión. Las transmisiones son legibles, escritas o ambas.
El primer contacto con la transmisión comenzó con el UNIX temprano. Décadas de práctica han demostrado que la idea de Stream puede simplemente desarrollar algunos sistemas enormes.
En UNIX, Stream se implementa a través de "|". En el nodo, como módulo de flujo incorporado, se utilizan muchos módulos centrales y módulos tres partes.
Al igual que UNIX, la operación principal de la transmisión de nodo es .Pipe (), y los usuarios pueden usar el mecanismo de contrapresión para controlar el equilibrio entre lectura y escritura.
Stream puede proporcionar a los desarrolladores una interfaz unificada que pueda reutilizar y controlar el equilibrio de lectura y escritura entre las transmisiones a través de interfaces de transmisión abstracta.
Una conexión TCP es tanto una transmisión legible como una secuencia de escritura, mientras que una conexión HTTP es diferente. Un objeto de solicitud HTTP es una secuencia legible, mientras que un objeto de respuesta HTTP es un flujo de escritura.
El proceso de transmisión de flujo se transmite en forma de un búfer de forma predeterminada, a menos que establezca otros formularios de codificación, el siguiente es un ejemplo:
La copia del código es la siguiente:
var http = require ('http');
var server = http.createServer (function (req, res) {
res.WriteHeader (200, {'Content-type': 'Text/Plain'});
res.end ("¡Hola, Big Bear!");
});
servidor.listen (8888);
console.log ("servidor HTTP que se ejecuta en el puerto 8888 ...");
Después de ejecutar, aparecerá el código confuso porque el conjunto de caracteres especificado no está configurado, como: "UTF-8".
Solo modifíquelo:
La copia del código es la siguiente:
var http = require ('http');
var server = http.createServer (function (req, res) {
Res.WriteHeader (200, {
'Content-type': 'Text/Plain; Charset = UTF-8' // Agregar charset = utf-8
});
res.end ("¡Hola, Big Bear!");
});
servidor.listen (8888);
console.log ("servidor HTTP que se ejecuta en el puerto 8888 ...");
Resultados de ejecución:
Por qué usar Stream
La E/S en el nodo es asíncrona, por lo que leer y escribir en el disco y la red requiere leer y leer datos a través de funciones de devolución de llamada. El siguiente es un ejemplo de descarga de archivo
En el código:
La copia del código es la siguiente:
var http = require ('http');
var fs = require ('fs');
var server = http.createServer (function (req, res) {
fs.ReadFile (__ dirname + '/data.txt', function (err, data) {
res.end (datos);
});
});
servidor.listen (8888);
El código puede implementar las funciones requeridas, pero el servicio debe almacenar en caché los datos completos de la memoria antes de enviar los datos del archivo. Si el archivo "data.txt" es muy
Si es grande y tiene una gran concurrencia, se desperdiciará mucha memoria. Debido a que el usuario debe esperar hasta que todo el archivo esté en caché a la memoria para aceptar los datos del archivo, esto lleva a
La experiencia del usuario es bastante mala. Afortunadamente, ambos parámetros (REQ, RES) son transmisión, por lo que podemos usar fs.CreateReadStream () en lugar de fs.ReadFile (). como sigue:
La copia del código es la siguiente:
var http = require ('http');
var fs = require ('fs');
var server = http.createServer (function (req, res) {
var stream = fs.CreateReadStream (__ dirname + '/data.txt');
stream.pipe (res);
});
servidor.listen (8888);
El método .Pipe () escucha los eventos 'Data' y 'End' de Fs.CreateReadStream (), para que el archivo "data.txt" no sea necesario almacenar en caché.
Se puede enviar un archivo al cliente inmediatamente después de completar la conexión del cliente. Otro beneficio de usar .pipe () es que se puede resolver cuando un cliente
Leer y escribir desequilibrio causado por un retraso final muy grande.
Hay cinco transmisiones básicas: legibles, escritas, transformadas, dúplex y "clásico". (Consulte la API para obtener más detalles)
2. Introducir ejemplos
Cuando los datos que deben procesarse no se pueden cargar en la memoria al mismo tiempo, o cuando el procesamiento es más eficiente mientras lee, necesitamos usar flujos de datos. NodeJS proporciona operaciones en flujos de datos a través de varias transmisiones.
Tomando el programa de copia de archivo grande como ejemplo, podemos crear un flujo de datos de solo lectura para la fuente de datos, el ejemplo es el siguiente:
La copia del código es la siguiente:
var rs = fs.CreateReadStream (PathName);
rs.on ('data', function (chunk) {
Dosomething (fragmento); // Use los detalles específicos como desee
});
rs.on ('end', function () {
limpieza ();
});
Los eventos de datos en el código se activarán continuamente, independientemente de si se puede procesar la función de tendhos. El código se puede modificar de la siguiente manera para resolver este problema.
La copia del código es la siguiente:
var rs = fs.CreateReadStream (SRC);
rs.on ('data', function (chunk) {
rs.Pause ();
Dosomething (fragmento, function () {
Rs.Resume ();
});
});
rs.on ('end', function () {
limpieza();
});
Se agrega una devolución de llamada a la función de dos tendos, por lo que podemos detener la lectura de datos antes de procesar los datos y continuar leyendo los datos después de procesar los datos.
Además, también podemos crear un flujo de datos de solo escritura para el objetivo de datos, de la siguiente manera:
La copia del código es la siguiente:
var rs = fs.CreateReadStream (SRC);
var ws = fs.CreatewriteReam (dst);
rs.on ('data', function (chunk) {
ws.write (fragmento);
});
rs.on ('end', function () {
ws.end ();
});
Después de que Dosomething se reemplaza escribiendo datos en la transmisión de solo escritura, el código anterior parece un programa de copia de archivo. Sin embargo, el código anterior tiene los problemas mencionados anteriormente. Si la velocidad de escritura no puede mantenerse al día con la velocidad de lectura, solo escribir el caché dentro del flujo de datos explotará. Podemos juzgar si los datos entrantes se han escrito al objetivo o se han colocado temporalmente en la memoria caché en función del valor de retorno del método .write, y juzgar cuando solo se han escrito los datos de escritura en el objetivo en función del evento de drenaje y pasar los próximos datos que se escribirán. Por lo tanto, el código es el siguiente:
La copia del código es la siguiente:
var rs = fs.CreateReadStream (SRC);
var ws = fs.CreatewriteReam (dst);
rs.on ('data', function (chunk) {
if (ws.write (chunk) === falso) {
rs.Pause ();
}
});
rs.on ('end', function () {
ws.end ();
});
ws.on ('drene', function () {
Rs.Resume ();
});
Finalmente, se realiza la transferencia de datos del flujo de datos de solo lectura al flujo de datos de solo escritura, y se incluye el control de almacén a prueba de explosión. Debido a que hay muchos escenarios de uso, como el programa de copia de archivo grande anterior, NodeJs proporciona directamente el método .Pipe para hacerlo, y su método de implementación interna es similar al código anterior.
Aquí hay un proceso más completo de copia de archivos:
La copia del código es la siguiente:
var fs = require ('fs'),
ruta = requerir ('ruta'),
out = process.stdout;
var filepath = '/bb/bigbear.mkv';
var readstream = fs.CreateReadStream (FilePath);
var writeStream = fs.CreateWriteStream ('file.mkv');
var stat = fs.StatSync (FilePath);
var totsize = stat.size;
var pasamente longitud = 0;
var dastSize = 0;
var starttime = date.now ();
readstream.on ('data', function (chunk) {
paseLength += shunk.length;
if (writeStream.Write (Chunk) === False) {
readStream.Pause ();
}
});
readstream.on ('end', function () {
WriteStream.end ();
});
WriteStream.On ('Drain', function () {
readstream.resume ();
});
setTimeOut (función show () {
Var porcentaje = math.ceil ((paseLength / TotalSize) * 100);
tamaño var = math.ceil (paseLength / 1000000);
var diff = tamaño - dureSize;
dastSize = tamaño;
out.clearline ();
out.cursorto (0);
out.write ('completado' + tamaño + 'mb,' + porcentaje + '%, velocidad:' + diff * 2 + 'mb/s');
if (passlength <totalSize) {
setTimeOut (espectáculo, 500);
} demás {
var endtime = date.now ();
console.log ();
console.log ('When Compartido:' + (EndTime - Starttime) / 1000 + 'Seconds.');
}
}, 500);
Puede guardar el código anterior como "copy.js". Experimento: agregamos un setTimeOut recursivo (o usamos directamente SetInterval) para ser un espectador.
Observe el progreso de finalización cada 500 ms y escriba el tamaño, el porcentaje y la velocidad de copia completos en la consola juntos. Cuando se completa la copia, se calcula el tiempo total.
Tres, resumamos
(1) Comprender el concepto de transmisión.
(2) Competente en el uso de la API de transmisión relevante
(3) Preste atención al control de los detalles, como: Copiar archivos grandes, utilizando la forma de "datos de fragmentos" para fragmentar.
(4), el uso de tubería
(5), enfatice un concepto nuevamente: una conexión TCP es tanto una secuencia legible como una transmisión de escritura, mientras que una conexión HTTP es diferente. Un objeto de solicitud HTTP es una secuencia legible, mientras que un objeto de respuesta HTTP es un flujo de escritura.