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
- streaming your HTML content and
- using the Content-disposition header to indicate that you want the file to be downloaded
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}`);
});