Display uploaded file using Node and Express - node.js

I have some code written that takes a single image file from index.html through an HTML form, adds a border to it using the gm module and saves it on the server. Now I'm trying to find a way to display it back to the user, preferably on the same page it was uploaded.
const express = require('express');
const app = express();
const multer = require('multer');
const gm = require('gm').subClass({imageMagick: true});
app.use(express.static(__dirname + '/public'))
app.use(express.static(__dirname + '/output'))
const upload = multer({
dest: 'temp/'
});
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
app.post('/', upload.single('file-to-upload'), (req, res) => {
var temp = req.file['path'];
var output = __dirname + '/output/' + req.file['filename'] + '.png'
console.log(req.file)
gm(temp)
.setFormat('png')
.resizeExact(512,512)
.composite(__dirname + '/masks/border.png')
.write(temp, function() {
gm(512, 512, 'none')
.fill(temp)
.drawCircle(256, 256, 256, 0)
.write(output, function(err) {
console.log(err || 'done');
});
});
});
app.listen(3000);

Right now your Express route never responds, it leaves the connection to the browser hanging. The key here is to respond with an HTML document. The laziest way to do that is (continuing at the end of your function):
.write(output, function(err) {
console.log(err || 'done');
return res.send(`
<title>My Image Is Neat!</title>
<h1>My Image Is Neat!</h1>
<img src="/${req.file['filename'] + '.png'}" />
`);
});
/${req.file['filename'] + '.png'} works because your use of express.static is mapping the /output folder into the root. You might want to add /uploads as the first argument so that the paths begin with /uploads and are less easily confused with other things.
As for including it in the page from which they uploaded it, you can write a function to send a similar backticked string for the original form, or you can get a little less lazy and use a templating language like Pug or Nunjucks to break these out to separate files.
Your approach so far implies you are not creating a single-page application with React, Vue, etc. but rather building old-school HTML forms. But if your real goals involve those frameworks you will need to change your approach to create an API rather than render pages.

Related

How to render my ejs view with response image from node js

I am using gridfs and mongoDB to store images in chunks. And whenever a user requests my server just sends a streamlined image file in response by piping.
Currently my code looks like this :
const download = async (req, res)=>{
try{
const fileName = req.params.name
await mongoClient.connect();
const database = mongoClient.db(dbConfig.database)
const bucket = new GridFsBucket(database, { // required for important methods like openDownloadStream
bucketName:dbConfig.imgBucket
})
const downloadStream = bucket.openDownloadStreamByName(fileName);
downloadStream.pipe(res) // it only displays an jpg/png image
// res.render("image") , I want to render this ejs page with a image in it and with some static content in it. I want to stream image
} catch(err){
res.status(501).render("error",{error: err})
}}
My output looks like :
my code output
It only renders a jpg file, like how above link is working see it.
but rather what i want to do is to get image from response object and render it with my other html elements.
Looks like you're trying to do too much at once.
You need to de-couple the desired streaming image from the initial rendering of your template. Include an image tag in your tempalte with a distinct api from which the image will stream, and the result will look something like your example.
Say your image is called test.png, your server index.js, and your ejs teplate index.ejs. The template (for the sake of your question) can be very simple: index.ejs-->
<h1> stream that image below! </h1>
<image src="/image" hieght="200px" width="200px";/>
Notice the src of this image - this will hit a distinct api on your backend that will stream the image.
The server index.js will look like this -->
var exp = require("express");
var fs = require("fs");
var app = exp();
app.set("view engine", "ejs");
app.get("/image", (req, res) => {
const streamReadFile = fs.createReadStream("test.png");
streamReadFile.pipe(res);
});
app.get("/", (req, res) => {
res.render("index");
});
app.listen(8080, () => {
console.log("listening on *:8080");
});
Notice here at the home route you render the template as
app.get("/", (req, res) => {
res.render("index");
});
The src in the image of your template then makes a request back to the server, hitting the /image route which will stream the desired image to your html
app.get("/image", (req, res) => {
const streamReadFile = fs.createReadStream("test.png");
streamReadFile.pipe(res);
});
Here's a working demo of the example above, where your image is streamed to an ejs template.

Multiple File Upload and rename by multer in node.js

I intended to use multer to upload multiple file and then rename them back to their original names. The below are the sample code:
var express = require('express');
var app = express();
var fs = require("fs");
var multer = require('multer');
app.use(express.static('public'));
var upload = multer({ dest: './upload/' });
app.get('/index.html', function (req, res) {
res.sendFile(__dirname + "/" + "index.html");
})
app.post('/file_upload', upload.array('theFile', 2), function (req, res, next) {
var errorcode = 0;
for (var i = 0; i < req.files.length; i++) {
fs.rename(req.files[i].path, req.files[i].destination + req.files[i].originalname, function (err) {
errorcode = err;
}(i));
}
if (errorcode != 0) {
console.log("errorcode is " + errorcode);
res.sendStatus(500);
return;
} else {
res.json({
message: 'File uploaded successfully',
});
}
})
var server = app.listen(8089, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
I'm testing the above code on a windows server. And my observation is that the files can be uploaded successfully but the fs.rename() keeps returning error "1". And the renamed files in the targeted folder are always 1Kb. It seems that the rename function intends to fetch the files which might be still uploading. I'm not sure whether my understanding is correct. If so, is there a way to determine whether the files have been uploaded completely? Any suggestion for my problem?
Why not use Multer's built-in renaming functionality?
Adapted from the documentation:
var storage = multer.diskStorage({
destination: '/path/to/uploads/folder',
filename: function (req, file, cb) {
// Here we specify the file name to save it as
cb(null, file.originalname);
}
})
// And we can use it for example like this:
app.post('/upload', upload.single('image'), function (req, res, next) {
// req.file is the `image` file
// req.body will hold the text fields, if there were any
})
However, there are a couple things you should be aware of if you take this approach:
The client can send any type of file, with any (potentially incorrect) extension. This is a potential security risk.
If two files are uploaded with the same name, the second file will overwrite the first.
If you serve these files to other users, the security risk greatly increases. An attacker could create a script or HTML page and upload it, possibly giving it a different file name extension. There are several ways it could be run, such as if the user tries to open it in a new tab because an image didn't show up. The full implications of this, and how to deal with it, are a topic of their own.
Finally, make very, very, sure that the user cannot write to a directory other than the uploads folder. What happens when file.originalname is something like ../../index.js? It may be better to convert the file name to a slug.

How can I make standalone, executable js files with Node js Express without defining the routes?

I'm having trouble with Node JS.
With the Apache/php model, I'm able to make a standalone save.php file (takes a post request, saves to a txt file) without fooling around with the Apache server.
<?php file_put_contents ( 'content.txt' , $_POST['myData']);?>
In Node, I have server.js started to serve up whatever files I have in /public :
var express = require('express');
var app = express();
app.use(express.static('public'));
app.use(express.static(__dirname + '/public'));
app.use(function(req,res, next){
if(req.accepts('html')){
res.status(404).sendFile(__dirname+'/public/404.html');
}
});
app.listen(process.env.PORT || 80);
How can I make a save.js file, e.g. /public/test_project1/save.js, that can be executed on an HTML form submission?
var fs = require('fs');
fs.writeFile("content.txt", ???post data???)
Is there any way to avoid explicitly defining the app.post()... in server.js every time I make a new js file in /public? I'm looking for an architecture that allows me to create one node server to host various separate project files in /public
First you need to create a endpoint for your post request, then parse form data, a option could be body-parser middleware , finally save the content.
Something like:
var express = require('express');
var app = express();
var bodyparser = require("body-parser");
app.use(bodyparser.urlenconded({extended:true}); // middleware for parse form data
app.use(express.static('public'));
app.use(express.static(__dirname + '/public'));
app.post("/endpoint",function(req,res){
//myData is input name
var content = req.body.myData;
fs.writeFile("content.txt", content,function(err){
res.end("done");
})
})
app.use(function(req,res, next){
if(req.accepts('html')){
res.status(404).sendFile(__dirname+'/public/404.html');
}
});
app.listen(process.env.PORT || 80);
Then you make a POST request to /endpoint with myData as input.
Here's what I came up with - modifying server.js to handle any POST requests within that folder. Is this dangerous?
app.post('/:folder/:file',function(req, res){
filepath=__dirname+'/public/'+req.params.folder+'/'+req.params.file;
fs.stat(filepath, function(err, stat) {
if(err == null) {
console.log('File exists');
var anyFile=require(filepath)(req.body);
res.redirect(anyFile);
} else if(err.code == 'ENOENT') {
res.send('File does not exist.');
} else {
res.send('Error: '+ err.code);
}
});
});

NodeJS/Express can't set headers after they are sent error when I upload files

I am working on a small web portal, and I've run into an error when I am uploading files(images) > ~25kb. I've tested the system, and it works great with small images, but then when I try a larger image, everything breaks.
I am using the latest version of node, express 3.4.0, multer 0.0.7 and here is the code in question:
Server:
var express = require('express'),
multer = require('multer'),
api = require('./routes/api.js'),
http = require('http'),
path = require('path'),
app = express(),
server = http.createServer(app);
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.set('port', process.env.VCAP_APP_PORT || 7000);
app.use(multer({dest: __dirname + '/uploads/'}))
app.use(app.router);
app.configure(function()
app.use("/uploads",express.static(__dirname + '/uploads'));
app.post('/api/upload/file', api.upload);
app.use(function(err, req, res, next) {
if(!err) return next();
console.log(err.stack);
res.json({error: true});
});
app.use(multer({
dest:'./uploads/',
rename: function (fieldname, filename) {
return filename.replace(/\W+/g, '-').toLowerCase();
}
}));
});
server.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
And the api.upload function:
exports.upload = function(req, res) {
console.log("Upload");
var resumeName = null,
picName = null;
console.log("File: " + JSON.stringify(req.files));
if(req.files.resume.size >= 0 || req.files.pic.size >= 0) {
if(req.files.resume.size > 0) {
resumeName = req.files.resume.name;
}
if(req.files.pic.size > 0) {
picName = req.files.pic.name;
}
console.log("Got file");
}
console.log("sending...");
res.send({picName: picName, resumeName: resumeName});
}
If it helps at all, my front end is angular, and I am using restangular and ng-upload to assist with from submitting and uploading files.
You are configuring your application inside app.configure()
app.configure(function()
app.use("/uploads",express.static(__dirname + '/uploads'));
app.post('/api/upload/file', api.upload);
app.use(function(err, req, res, next) {
if(!err) return next();
console.log(err.stack);
res.json({error: true});
});
app.use(multer({
dest:'./uploads/',
rename: function (fieldname, filename) {
return filename.replace(/\W+/g, '-').toLowerCase();
}
}));
});
and outside of it.
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.set('port', process.env.VCAP_APP_PORT || 7000);
app.use(multer({dest: __dirname + '/uploads/'}))
app.use(app.router);
Notice that you have defined the multer middleware twice.
I guess multer must be defined before app.use(app.router). The second definition is causing the sending of headers after they have been sent.
NOTE: app.configure() is deprecated and was even removed beginning in express 4.x. I do not recommend its usage. You better define all your middleware outside.
TL;DR There was (what amounted to) a race case in saving the uploads on the server. Uploading files individually fixed that.
I ended up following #Leonel's advice and moving to connect-busboy. Initially I had the same issue, then I realized what was happening. I use ng-upload in the front end, but ng-upload will upload all the files in one go. Since the form I was using had two (optional) files that a user could upload, the backend uploading service would be hit with two files in req.files. Since my implementation of multer didn't plan for this too well, it would try to handle both, but would only send one response (which ever hit the res.send line first).
To fix this, I moved to angular-file-upload which allows me to have more options over how I upload files (will upload individually). In addition, I used safwanc's solution to using connect-busboy for file uploads.

Node express requests

im new to node and after a few weeks practicing it, i found the express framework and i started to used it because it handle the tools i need more easy, but theres something i dont get to understand, its quite different from how do make the app without express. i dont quite get the express api well (its a bit confusing). im trying to make the request url to be found in specific url (./views). so when logi is requested, then it will do (./views/login.html)when url is(hostname/login), and so on if it contains folder. this is my code
/*
Chat application for #node.js
express version.
*/
//Load modules.
var express = require('express'),
socket = require('socket.io'),
swig = require('swig'),
fs = require('fs');
//Load config.
console.log('Loading configuration.');
var config = fs.readFileSync('config.json');
var config = JSON.parse(config);
var port = config.port;
var views = config.views;
console.log('Configuration loaded.');
//Initiate express module in app.
var app = express();
// app.get('/', function(request, response)
// {
// fs.readFile('./views/index.html', function(error, data)
// {
// if(error)
// {
// response.send('View cannot be rendered.');
// }
// response.type('html');
// response.send(data);
// });
// });
var test = "Hello";
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
swig.setDefaults(
{
cache: false
});
app.get('/', function(request, response)
{
response.render('index',
{
//Var to be named in the render : value;
'test': test,
'Title': 'Testing page',
});
});
//logger.
app.use(function(request, response, next)
{
console.log('%s %s', request.method, request.url);
next();
});
//Set directory for static files (css, js, img);
app.use(express.static(__dirname + '/public'));
//Run the app.
app.listen(port);
im using swig module for dynamic data in html, im also comenting my tests, used app.use() for static files in ./public folder (as found in the api). so what i want to achieve is, if the url file exist, then render it with its static files(css, js). if not, return a custom html file..
im using app.get() to recieve the expecific request.(which totally i dont get).
PD: PLEASE, NEED EXPRESS TUTORIALS (better than express homepage itself.), atleast for newbies.
Since views is not in the public directory, any url with views in it will not go to the app.use() function anyway (because it can't find it there). This is good. What you need to do now is create a routing function for that specific view. Something like this above your app.use():
app.get('/views/login', function(req, res){
res.render(__dirname + 'views/login');
});
usually, rendering engines will allow you to do a shortcut though, and just do res.render('login'); and it will find it in views by itself.
Also, some renderers allow you to specify a directory to look for when requests come in. I don't know if swig offers this though.

Resources