Home > OS >  Create and download html file without saving it in filesystem with Node/Express
Create and download html file without saving it in filesystem with Node/Express

Time:02-11

I have an express app that generates an HTML file in the filesystem and then downloads it. https://node-page-generator.herokuapp.com/

Is there any way to create and download a file without relying on the filesystem to save and then retrieve it?

The app uses fs.writefile():

import fs from 'fs';

export const callFileSystem = (path: string, content:string) => {
  fs.writeFile(
    `${path}`,
    `${content}`,
    function (err: any) {
      if (err) throw err;
      console.log('file created');
    }
  )
}

A POST HTTP request on /generate send all page properties to the generatePage function that creates an index.html file

import { pageProperties } from './pageProps';
import { callFileSystem } from '../../helpers/fileSystem';

export const generatePage = (props: pageProperties) => {
  const path = 'index.html';

  callFileSystem(
    path,
    `
  <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <link rel="icon" href="${props.logoUrl}" type="image">
            <title>${props.title}</title>
            <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
        </head>
        <body>
            <style>
                :root {
                    --navbar-color: ${props.navbar.backgroundColor};
                    --background-color: ${props.pageBackgroundColor};
                    --font-color: ${props.fontColor};
                }
                html, body, h1, h2 {
                    margin: 0;
                }
                h1, h2, h3, h4, h5, p, span {
                    text-align: center;
                    color: var(--font-color, title, logoUrl, cardContent);
                }
                html {
                    margin: 0;
                    padding: 0;
                }
                body {
                    margin: 0;
                    padding: 0;
                    background-color: var(--background-color, title, logoUrl, cardContent);             
                }
                .navbar {
                    background-color: var(--navbar-color, title, logoUrl, cardContent) !important;                
                }
            </style>
    
    <nav >
        <div >
          <a  href="#">
            <img src="${props.logoUrl}" alt="" width="30" height="24">
            <h1>${props.title}</h1>
          </a>
        </div>
      </nav>     
      <div >
    
      </div>       
        </body>
        </html>
  `
  );
}

Then a GET request at /download downloads the generated file:

export const downloadPage = (response: Resp) => {
  const file = 'index.html';
  response.download(file);
}

I tried calling fs.writefile() as the path argument for res.download(), but it didn't work.

While the app kind of works as intended, the problem with this method is that if two /generate requests are made roughly simultaneously, the first user may call /download too late and get the response intended for the second user.

CodePudding user response:

You can achieve this by

const stream = require("stream");
const express = require("express");
const app = express();
const port = 3000;

const htmlString = `
<!DOCTYPE html>
<html lang="en">
   <head>
      <title>My Title</title>
   </head>
   <body>My HTML Content</body>
</html>`;

app.get("/", (req, res) => {
  const readStream = new stream.PassThrough();
  readStream.end(htmlString);

  res.set("Content-disposition", "attachment; filename=index.html");
  readStream.pipe(res);
});

app.listen(port, () => {
  console.log(`App listening on port ${port}`);
});
  • Related