How to send output image from node js child process to 'parent'? - javascript

I have a child process that is being called as follows:
server.js
app.get('/search', function (req, res) {
var cp = spawn('node', 'app.js');
cp.stdout.on('data', function(data) {
console.log('stdout: ' + data);
});
...
});
Currently app.js is utilizing chrome-headless' captureScreenshot function to generate a screenshot, which is then stored locally.
app.js:
const img = await Page.captureScreenshot({format: 'png', fromSurface: true});
fs.writeFile('./screenshot.png', img.data, 'base64', function (err) {
if (err) {
console.log(err);
}
});
It is not necessary to store the image locally, but rather upload it to a server. I am trying to find a way to get this functionality to work:
server.js V2
app.get('/search', function (req, res) {
var cp = spawn('node', 'app.js');
cp.stdout.on('data', function(data) {
upload_image(data);
});
...
});
app.js V2
const img = await Page.captureScreenshot({format: 'png', fromSurface: true});
//EXPOSE 'img' to parent via cp.stdout - How do I do this part?
});

You can send messages via fork()
server.js
const { fork } = require('child_process');
app.get('/search', function (req, res) {
const forked = fork('app.js');
forked.on('message', (data) => {
uploadImage(data.imageURL); // It will be send by parent.
}); ...
});
app.js
const img = await Page.captureScreenshot({format: 'png', fromSurface: true});
fs.writeFile('./screenshot.png', img.data, 'base64', function (err) {
if (err) {
console.log(err);
}
process.send({ imageURL: './screenshot.png' }); //You can send any data from here.
});
Here is the nice tutorial for you.
https://medium.freecodecamp.org/node-js-child-processes-everything-you-need-to-know-e69498fe970a
I hope it helped you.

Related

What is the correct way to make multer work with Node and Express here?

I am trying to create a route through which I can upload photos. However as I made so,e changes it stopped working and I am not sure how to make it work.
const multer = require('multer');
// MULTER STORAGE
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, '/upload');
},
filename: (req, file, cb) => {
const ext = file.mimetype.split('/')[1];
// Saving format: user-UserId-DateStamp.ext
//e.g user-608d55c7e512b74ee00791de-1621992912638.jpeg
cb(null, `user-${req.body.userId}-${Date.now()}.${ext}`);
},
});
//MULTER FILTER
const multerFilter = (req, file, cb) => {
//mimetype always starts with image/ then png or jpeg or..
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cb(new AppError('You are only allowed to upload image files.', 400), false);
}
};
const uploadDirectory = multer({
storage: multerStorage,
fileFilter: multerFilter,
});
//exports.uploadPhoto = uploadDirectory.single('photo');
//app.use(express.static('./uploads'));
// INCLUDE ERROR CLASS AND ERROR CONTROLLER
const AppError = require('../utils/appError.js');
const errorController = require('./errorController.js');
const { Mongoose } = require('mongoose');
The main problem Im guessing is in this block
//UPLOAD PHOTO
exports.uploadPhoto = uploadDirectory(async (req, res) => {
console.log(req.body);
console.log(req.file);
try {
const newPhoto = await photoModel.create(req.file);
newPhoto.save().then((result) => {
console.log('Saved');
res.status(201).json({
status: 'success',
// data: JSON.parse(JSON.stringify(newPhoto.file)),
});
});
} catch (err) {
console.log('Error in upload');
errorController.sendError(err, req, res);
}
}).single('photo');
Can anybody let me know how to correctly write the exports.uploadPhoto
Originally the last function looked like this
exports.uploadPhoto = async (req, res) => {
console.log(req.body);
console.log(req.file);
try {
const newPhoto = await photoModel.create(req.file);
newPhoto.save().then((result) => {
console.log('Saved');
res.status(201).json({
status: 'success',
// data: JSON.parse(JSON.stringify(newPhoto.file)),
});
});
} catch (err) {
console.log('Error in upload');
errorController.sendError(err, req, res);
}
};
The multer middleware function, in your case uploadDirectory, is usually used before other middleware functions/controllers where you have your business logic (e.g. uploadPhoto).
app.post('/upload', uploadDirectory.single('photo'), uploadPhoto);
Keep your original uploadPhoto function and with the above code you'll have access to the data and file through reg.body and req.file, respectively.
This Request Parsing in Node.js Guide (it's free) will help you with file uploads in Node.js.

Download JSON file from a node express server through my frontend

I have stored the file after uploading it to the downloads folder in my project directory.
I want to download that saved file from the frontend.
When I click on the download button, it doesn't fetch the file.
And when I go to http://localhost:5000/download on the express app, I got this error message
Error: Can't set headers after they are sent.
Express Server Code:
app.get('/download', (req, res) => {
res.send('file downloaded')
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
Frontend App code:
HTML:
<button class="download-btn">download</button>
Script:
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/http://localhost:5000/download");
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);
Folder Structure:
Update:
Server.js
const uploadFiles = async (req, res) => {
const file = await req.files[0];
console.log(file)
postmanCollection = file.path;
outputFile = `downloads/${file.filename}.yml`
convertCollection();
res.json({ message: "Successfully uploaded files" });
}
app.post("/upload_files", upload.array("files"), uploadFiles);
Anyone please help me with this.
You are already using res.send ,which sends the response headers back to client ,which ends the request response cycle ,and when you try to do res.download it throws error. Use instead
app.get('/download', (req, res) => {
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
res.send('file downloaded')--->remove this line
You need to update your js code as well
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/download"); //http://localhost:5000--->this is not required
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);

Trying to serve png in Node.js: Cannot read property 'end' of undefined

I'm a Node, web dev and programming beginner following 'Get Programming with Node.js' by Jonathan Wexler. I've worked up to the end of the section on routing in vanilla Node, with the following code in main.js:
http = require("http"),
httpStatusCodes = require("http-status-codes"),
fs = require("fs"); // This is a node js core module which interacts with the filesystem on behalf of the app.
(router = require("./router")),
(plainTextContentType = {
"Content-Type": "text/plain",
}),
(htmlContentType = {
"Content-Type": "text/html",
}),
(pngContentType = {
"Content-Type": "image/png",
}),
(customReadFile = (file, res) => {
fs.readFile(`./${file}`, (errors, data) => {
if (errors) {
console.log("Error reading the file...");
}
res.end(data);
});
});
router.get("/", (req, res) => {
res.writeHead(httpStatusCodes.OK, plainTextContentType);
res.end("INDEX");
});
router.get("/index.html", (req, res) => {
res.writeHead(httpStatusCodes.OK, htmlContentType);
customReadFile("views/index.html", res);
});
router.get("/image.png", (req, res) => {
res.writeHead(httpStatusCodes.OK, pngContentType);
customReadFile("public/images/image.png");
});
router.post("/", (req, res) => {
res.writeHead(httpStatusCodes.OK, plainTextContentType);
res.end("POSTED");
});
http.createServer(router.handle).listen(3000);
console.log(`The server is listening on port number: ${port}`);
It works just fine for loading the /index.html file, but when I try to load /image.png (stored in the correct folder, public/images/image.png) it always crashes with the error message TypeError: Cannot read property 'end' of undefined.
This happens no matter what image I use and happens also if I move the image to another folder, such as views. I have also tried writing res.write(data); res.end() instead, also to no avail. I have also tried using an async function within fs.readFile but I don't think that should be the issue here, because fs.readFile should itself execute synchronously within the function if I understand correctly.
Edit
The code for router.js, in case that helps:
const httpStatus = require("http-status-codes"),
htmlContentType = {
"Content-Type": "text/html",
},
routes = {
GET: {
"/info": (req, res) => {
res.writeHead(httpStatus.OK, {
"Content-Type": "text/plain",
});
res.end("Welcome to the Info Page!");
},
},
POST: {},
};
exports.handle = (req, res) => {
try {
if (routes[req.method][req.url]) {
routes[req.method][req.url](req, res);
} else {
res.writeHead(httpStatus.NOT_FOUND, htmlContentType);
res.end("<h1>No such file exists</h1>");
}
} catch (ex) {
console.log("error: " + ex);
}
};
exports.get = (url, action) => {
routes["GET"][url] = action;
};
exports.post = (url, action) => {
routes["POST"][url] = action;
};
You have to pass the res object as a second argument to your customReadFile function to make it work. What you have:
router.get("/image.png", (req, res) => {
res.writeHead(httpStatusCodes.OK, pngContentType);
customReadFile("public/images/image.png"); // <-- 2nd arg missing
});
What you want to have:
router.get("/image.png", (req, res) => {
res.writeHead(httpStatusCodes.OK, pngContentType);
customReadFile("public/images/image.png", res); // <-- 2nd arg added
});

NodeJS - 'Error: Invalid URI "/"'

I'm using request-promise to request two JSON data files which exist locally in my local project directory folder.
ie:
However, I am getting a 500 internal server error, when trying to pass the data to the view and my node console outputs 'Error: Invalid URI "/"',
Please see below:
server.js
let express = require('express');
let app = express();
let path = require('path');
const rp = require("request-promise");
//STORE PATH for local JSON files on variables
let guest = require('./public/data/Companies');
let hotel = require('./public/data/Guests');
app.set("port", process.env.PORT || 5000);
//GET JSON
//Question: Is it okay to pass uri:guest
app.get('/data', function(req, res) {
Promise.all([rp({uri: guest, json: true}), rp({uri: hotel, json: true})]).then(function([hotels, guests]) {
//res.json({hotels, guests});
res.send({hotels, guests});
console.log(hotels, guests);
}).catch(function(err) {
console.log(err);
res.status(500).end();
});
});
//CATCHALL
app.get("/*", function(req,res){
let file = req.params[0] || "/views/index.html";
res.sendFile(path.join(__dirname, "/public/", file));
});
//SET PORT
app.listen(app.get("port"), function(){
console.log("Listening on port: " , app.get("port"));
});
then on client.js:
$(function() {
$.ajax({
type: "GET",
url: "/data",
success: function (res) {
console.log(res);
}
});
});
Why do you use request to get the data? Why don't you use the filesystem module from Node.js (fs) to get the data? When you call rp(), you should pass an absolute URI and not a local path.
To use it in your code, you need to "promisify" the readFile function:
let readFileAsAPromise = function(filename){
return new Promise(function(resolve, reject) {
fs.readFile(filename, (data, err) => {
if(err) reject(err);
resolve(data)
})
})
}
You can then you Promise.all.
Why aren't you simply returning the variables?
I mean:
app.get('/data', function(req, res) {
res.send({hotels, guests});
});

pipe works on localhost but not on remote nodejs

I'm trying to use ytdl-core module in order to download youtube audio to my local disk (some path on my computer).
I created an API to which I can call with the requested youtube url and the destination folder in which I want the file to be saved.
app.get('/api/downloadYoutubeVideo', function (req, res) {
var videoUrl = req.query.videoUrl;
var destDir = req.query.destDir;
ytdl.getInfo(videoUrl, function(err, info){
var videoName = info.title.replace('|','').toString('ascii');
var stream = ytdl(videoUrl, { filter: 'audioonly'})
.pipe(fs.createWriteStream(destDir + '\\' + videoName + '.mp3'));
stream.on('finish', function() {
res.writeHead(204);
res.end();
});
});
});
The problem is that when I call the api on my localhost (example: localhost:11245/api/downloadYoutubeVideo?videoUrl=https://www.youtube.com/watch?v=E5kJDWQSBUk&destDir=C:\test)
it works and the file indeed downloads to "C:\test".
But when I call to the api on my remote (example: http://sometest.cloudno.de/api/downloadYoutubeVideo?videoUrl=https://www.youtube.com/watch?v=02BAlrAkuCE&destDir=C:\test)
it doesnt create the file in the directory...
I have searched the answer but haven't found one...
Does C:\test already exist on your remote? If not you can't use fs.createWriteStream() until the directory has been created, it will not create the directory for you implicitly. Since you're not listening for an 'error' event, you wouldn't even know that was the problem since you're not capturing it.
The below code sample will check for the existence of destDir and if it doesn't exist will create it before proceeding.
const fs = require('fs');
const sep = require('path').sep;
function checkAndMakeDir(dir, cb) {
fs.access(dir, (err) => {
if (err)
fs.mkdir(dir, (err) => {
if (err)
return cb(err);
return cb();
});
else
return cb();
});
}
app.get('/api/downloadYoutubeVideo', function (req, res) {
let videoUrl = req.query.videoUrl;
let destDir = req.query.destDir;
checkAndMakeDir(destDir, (err) => {
if (err) {
res.writeHead(500);
return res.end();
}
ytdl.getInfo(videoUrl, function (err, info) {
let videoName = info.title.replace('|', '').toString('ascii');
let saveStream = fs.createWriteStream(`${destDir}${sep}${videoName}.mp3`);
saveStream.once('error', (err) => {
console.log(err);
res.writeHead(500);
return res.end();
});
saveStream.once('finish', () => {
res.writeHead(204);
return res.end();
});
ytdl(videoUrl, {filter: 'audioonly'})
.once('error', (err) => {
console.log('Read Stream Error', err);
res.writeHead(500);
return res.end();
})
.pipe(saveStream);
});
});
});

Categories

Resources