web-dev-qa-db-fra.com

res.sendfile in Node Express avec transmission de données

Est-il possible de rediriger vers un fichier HTML à partir d'une application Node.JS avec quelque chose comme: res.sendFile of express et de transmettre des données JSON au fichier HTML? 

20
user3393991

Vous obtenez une réponse d'une demande donnée. Vous pouvez combiner plusieurs éléments dans une seule réponse ou demander au client de faire des demandes distinctes pour obtenir des éléments distincts.

Si vous essayez de prendre un fichier HTML et de le modifier en y insérant du JSON, vous ne pouvez pas utiliser simplement res.sendFile() car il ne fait que lire un fichier à partir du disque ou du cache et le diffuser directement comme réponse n'offrant aucune possibilité de le modifier. 

Le moyen le plus courant consiste à utiliser un système de modèles qui vous permet d'insérer des éléments dans un fichier HTML (en remplaçant généralement des balises spéciales par vos propres données). Il existe littéralement des centaines de systèmes de modèles et beaucoup prennent en charge node.js. Les choix courants pour node.js sont Jade (Pug), Guidon, Ember, Dust, EJS, Moustache.

Ou, si vous le souhaitez vraiment, vous pouvez lire le fichier HTML en mémoire, utiliser une sorte d'opération .replace() pour insérer vos propres données, puis res.send() le fichier modifié résultant.

10
jfriend00

Je sais que c'est tard, mais je voulais proposer une solution que personne d'autre n'a fournie. Cette solution permet de diffuser un fichier dans la réponse tout en vous permettant de modifier le contenu sans recourir à un moteur de création de modèles ni à la mise en mémoire tampon de l'intégralité du fichier.

Passez au bas de la page si vous ne vous souciez pas de "pourquoi"

Permettez-moi d’abord de décrire pourquoi res.sendFile est si désirable pour ceux qui ne le savent pas. Comme Node est à thread unique, il fonctionne en exécutant successivement de très petites tâches, ce qui inclut la lecture à partir du système de fichiers et la réponse à une requête http. Node n'interrompt à aucun moment son activité et lit l'intégralité du système de fichiers. Il va lire un peu, faire autre chose, lire un peu plus, faire autre chose. Il en va de même pour la réponse à une demande http et à la plupart des autres opérations dans Node (sauf si vous utilisez explicitement la version sync d'une opération - telle que readFileSync - ne le faites pas si vous pouvez l'aider sérieusement, ne le faites pas - c'est égoïste. ). 

Prenons un scénario dans lequel 10 utilisateurs font une demande pour le même fichier. Il serait inefficace de charger le fichier entier en mémoire, puis de l’envoyer à l’aide de res.send(). Même s'il s'agit du même fichier, le fichier serait chargé en mémoire 10 fois avant d'être envoyé au navigateur. Le ramasse-miettes devra alors nettoyer ce gâchis après chaque demande. Le code serait innocemment écrit comme ceci:

app.use('/index.html', (req, res) => {
   fs.readFile('../public/index.html', (err, data) => {
      res.send(data.toString());
   });
});

Cela semble juste et ça marche, mais c'est terriblement inefficace. Puisque nous savons que Node fait les choses par petits morceaux, la meilleure chose à faire serait d’envoyer les petits morceaux de données au navigateur à mesure qu’ils sont lus à partir du système de fichiers. Les morceaux ne sont jamais stockés en mémoire et votre serveur peut maintenant gérer des ordres de grandeur plus de trafic. Ce concept s'appelle la diffusion en continu et c'est ce que res.sendFile fait: il diffuse le fichier directement à l'utilisateur à partir du système de fichiers et laisse la mémoire libre pour des tâches plus importantes. Voici à quoi ça ressemble si vous le faites manuellement:

app.use('/index.html', (req, res) => {
    fs.createReadStream('../public/index.html')
    .pipe(res);
});

Solution

Si vous souhaitez continuer à diffuser un fichier à l'utilisateur tout en y apportant de légères modifications, cette solution est pour vous. Veuillez noter que ceci ne remplace pas un moteur de templates mais devrait plutôt être utilisé pour apporter de petites modifications à un fichier au fur et à mesure de sa diffusion. Le code ci-dessous va ajouter une petite balise de script avec des données au corps d'une page HTML. Il montre également comment ajouter ou ajouter du contenu à un flux de réponse http:

const Transform = require('stream').Transform;
const parser = new Transform();
parser._transform = function(data, encoding, done) {
  const str = data.toString().replace('</body>', '<script>var data = {"foo": "bar"};</script></body>');
  this.Push(str);
  done();
};

// app creation code removed for brevity

app.use('/index.html', (req, res) => {
    res.write('<!-- Begin stream -->\n');
    fs
    .createReadStream('../public/index.html')
    .pipe(parser)
    .on('end', () => {
        res.write('\n<!-- End stream -->')
    }).pipe(res);
});
25
Ryan Wheale

Eh bien, c'est un peu vieux, mais je n'ai pas vu de réponse suffisante, sauf pour "pourquoi pas". Vous avez moyen de passer des paramètres dans un fichier statique. Et c'est assez facile. Pensez à suivre le code sur votre origine (en utilisant express):

    let data = fs.readFileSync('yourPage.html');
    if(data)
    res.send(data.replace('param1Place','uniqueData'));
    //else - 404

Maintenant, par exemple, définissez un cookie dans yourPage.html , quelque chose comme:

    <script>
    var date = new Date();
    document.cookie = "yourCookieName='param1Place';" + 
    date.setTime(date.getTime() + 3600) + ";path=/";
    </script>

Et vous pouvez clairement extraire le contenu de uniqueData de votre nomCookie où vous voulez dans votre js

2
user6476673

Vous ne pouvez envoyer qu'une seule réponse du serveur. La chose la plus commune à faire serait de modéliser votre fichier sur le serveur avec des nunjucks ou des jades. Un autre choix consiste à rendre le fichier sur le client, puis à utiliser javascript pour effectuer un appel ajax au serveur afin d'obtenir des données supplémentaires. Je suppose que vous pouvez également définir des données dans un cookie, puis les lire côté client via javascript.

1
Robert Moskal

(Sauf si vous souhaitez modéliser le fichier HTML pour insérer les données JSON dans une balise de script). Vous devez exposer un point de terminaison api en exprimant l'envoi des données vers la page et disposer d'une fonction pour y accéder. par exemple,

// send the html
app.get('/', (req, res) => res.sendFile('index'));

// send json data
app.get('/data', (req, res) => res.json(data));

Maintenant, côté client, vous pouvez créer une demande pour accéder à ce noeud final.

function get() {
  return new Promise((resolve, reject) => {
    var req = new XMLHttpRequest();
    req.open('GET', '/data');
    req.onload = () => resolve(req.response);
  });
}           
 // then to get the data, call the function

get().then((data) => {
  var parsed = JSON.parse(data);
  // do something with the data
});

MODIFIER:

Donc, les fonctions de flèche ne fonctionnent probablement pas encore du côté client. assurez-vous de les remplacer par function () {} dans votre vrai code

0
Jonah Williams

Pourquoi ne pas simplement lire le fichier, appliquer des transformations puis configurer la route dans le rappel?

fs.readFile(appPath, (err, html) => {
   let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));

   app.get('/', (req, res) => {
       res.send(htmlPlusData);
   });
});

Notez que vous ne pouvez pas modifier dynamiquement data, vous devez redémarrer l'instance de noeud.

0
Brad