Estoy usando un servidor Node.js simple para enviar un archivo JSON grande al cliente, donde cada línea tiene un objeto JSON independiente. Me gustaría enviar este archivo al cliente una línea a la vez. Pero mi problema es que el servidor espera hasta que se haya llamado a response.end()
para enviar todo de una vez.
El código de mi servidor se ve así:
http.createServer(async function (request, response) { response.writeHead(200, {"Content-Type": "application/json; charset=UTF-8", "Transfer-Encoding": "chunked", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0}); response.write(JSON.stringify('["The first bit of JSON content"]\n')); response.write(await thisFunctionTakesForever()); response(end); }
Realmente no quiero que el usuario espere hasta que se haya cargado todo el archivo JSON antes de que mi secuencia de comandos pueda comenzar a analizar los resultados. ¿Cómo puedo hacer que mi servidor envíe los datos en fragmentos?
Información adicional: ¿Cómo sé que mi servidor Node.js no está enviando ninguna parte del archivo hasta que se haya llamado a response.end
?
Estoy usando XMLHttpRequest
para manejar los fragmentos a medida que llegan. Entiendo que http.responseText siempre crece con cada fragmento, así que lo filtro para encontrar las nuevas líneas que llegan cada vez:
let http = new XMLHttpRequest(); http.open('GET', url, true); http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); http.onreadystatechange = function() { if(http.readyState >= 3 && http.status == 200) { // Parse the data as it arrives, throwing out the ones we've already received // Only returning the new ones let json = http.responseText.trim().split(/[\n\r]+/g) let dataChunks = json.map(e => JSON.parse(e)); let newResults = []; for(let i=0; i<dataChunks.length; i++) { if(!previousResults.map(e => e[0]).includes(dataChunks[i][0])) { newResults.push(dataChunks[i]); } } previousResults = previousResults.concat(newResults); } } http.send();
La matriz resultados previousResults
debería crecer lentamente con el tiempo. Pero en cambio, hay un gran retraso, luego todo aparece de repente al mismo tiempo.
El siguiente hilo está relacionado. Pero desafortunadamente, ninguna de las soluciones propuestas resolvió mi problema... Node.js: codificación de transferencia fragmentada
Vi que estás usando codificación fragmentada: "Transfer-Encoding": "chunked"
. Este tipo de codificación transferirá cada fragmento individualmente. Es realmente posible escribir cada fragmento inmediatamente sin esperar a los demás.
Cada fragmento se encapsulará con el formato definido en el RFC 2612 por la biblioteca http
. En general, cada fragmento tiene una línea que indica el tamaño del fragmento después de <CR>, <LF>
. Luego puede enviar el contenido del fragmento. Y el último fragmento es una excepción que indica que todos los fragmentos están terminados.
Podría darte un ejemplo a continuación:
const http = require("http") function generateChunk(index, res, total) { setTimeout(() => { res.write(`<p> chunk ${index}</p>`) if (index === total) { res.end() } }, index * 1000) } function handlerRequest(req, res) { res.setHeader("Content-Type", "text/html; charset=UTF-8") res.setHeader("Transfer-Encoding", "chunked") let index = 0 const total = 5 while (index <= total) { generateChunk(index, res, total) index++ } } const server = http.createServer(handlerRequest) server.listen(3000) console.log("server started at http://localhost:3000")%
Si captura los paquetes TCP, verá diferentes fragmentos en diferentes paquetes TCP. No tienen ninguna dependencia.
Ver la imagen:
Sin embargo, el cliente HTTP (como el navegador) debe aceptar todos los fragmentos antes de entregarlos a la aplicación por las siguientes razones: una vez que se reciben todos los fragmentos, el servidor también podría enviar algunos encabezados: encabezados de tráiler . Esos encabezados incluyen Content-MD5
, Content-Length
, etc. El cliente debe verificar como Content-MD5
una vez que se reciben todos los fragmentos antes de entregarlos a la aplicación. Creo que es por eso que no puede recibir fragmentos uno por uno en el lado del navegador.