app.get('/get/image/*', (req, res) => {
const path = req.params[0];
const fName = path.substring(path.lastIndexOf('/') + 1);
res.setHeader('content-type', 'image/png');
res.download(uploadBasePath + path, fName);
})
This is downloading the images instead of displaying them over the browser. I want to display on the browser not do auto download. How can I fix it?
I'm not sure what exactly you're trying to do, but if you just want to display the image in the browser, there are several ways to do it, here is one example:
// Send image url in response
app.get('/get/image/*', (req, res) => {
const imgUrl = 'yourimgurlhere';
res.send({ imageUrl: imgUrl })
})
And in your frontend code you refer to that url.
Example using ejs:
<img src="/<%= imageUrl %>" alt="My image is now displayed in the browser">
You can read the image buffer and then respond the buffer to the client with the content type is image/jpeg.
const fs = require('fs');
...
app.get('/get/image/*', (req, res) => {
const path = req.params[0];
const fName = path.substring(path.lastIndexOf('/') + 1);
fs.readFile(uploadBasePath + fName, (err, buffer) => {
if (err) {
return res.status(500).send(err);
}
res.contentType('image/jpeg');
res.send(buffer)
})
})
This is really simple just send the file from response in express.
const path = require('path')
res.sendFile(path.join(__dirname, "public",'filepath.jpg'));
Related
Making a basic blog with an admin section to learn the basics of node and express. I just implemented multer middleware to save images for a blog post to a folder ("images") on the server - not to mongo or an s3 bucket - keeping it simple for now to learn.
I am using EJS and using res.render to send and render the frontend. However, I want to put the image in the EJS file as well. I've tried simply passing in the filename like so:
res.render(path.resolve(__dirname, "../", "views", "posts.ejs"), {postData, file});
postData being the data on the post from the mongodb collection. All this does is send the filename itself which is not helpful.
I've looked around, but don't seem to find an answer to this, or I'm over thinking this?
Here is the rest of the code for the controller:
const path = require("path");
const fs = require('fs');
const Post = require('../models/modelPosts');
exports.getPost = (req, res, next) => {
const postPath = req.params.post;
Post.findOne({ postPath: postPath }, (error, postData) => {
if (error) { return next(error) };
if (postData.postReadyToView == true) {
// find the correct image
fs.readdirSync('./images').forEach(file => {
const stringOfFile = JSON.stringify(file);
const stringOfPathJPEG = JSON.stringify(postPath + ".jpeg");
const stringOfPathJPG = JSON.stringify(postPath + ".jpg");
const stringOfPathPNG = JSON.stringify(postPath + ".png")
// send the ejs file and image
if (stringOfFile == stringOfPathJPEG ||
stringOfFile == stringOfPathJPG ||
stringOfFile == stringOfPathPNG) {
res.render(path.resolve(__dirname, "../", "views", "posts.ejs"), {
postData, file
});
}
})
} else {
res.redirect(404, "/404");
}
})
};
Send the file path of the page to be rendered as data, register the image garden folder (ex: public/images) as a static folder using express.static in nodejs, and load the image when the file path is loaded in the rendering page. I think you can.
Im posting two fields in Angular to a NodeJs endpoint.
I usually post on the body and everything is perfect at Node, but this time, I have to post a form to upload a file.
So this is my code for posting the form data (Angular side):
var APIURL = sessionStorage.getItem('endPoint') + "profile/updateeocoverage";
let formData = new FormData();
formData.append("data", JSON.stringify(this.payLoad));
if (this.eofile) {
formData.append("myfile", this.eofile, this.eofile.name);
}
this.httpClient.post(APIURL, formData).subscribe(
result => {
....
My problem is that I always retrieved the body at node as follows:
router.post('/updateeocoverage', async (req, res, next) => {
console.log(req.body)
return;
....
But with the method Im using now in Angular, req.body is retrieving {}
Is the POST wrong, or the router wrong at Node side?
Thanks.
UPDATE ON PABLO'S ANSWER BELOW (SOLUTION PROVIDED) for whoever runs into this issue:
Using Multer solved the problem, but as he said some workaround is needed as to set the file name, but most important it is required to authenticate the user, so:
const multer = require('multer')
const path = require('path')
To authenticate the user, I send the authentication parameters on the header. Sending it as formdata.append didn't work for me. This sets true or false to upload the file, otherwise anyone can upload anything to the route:
async function authenticateUser(req, file, cb) {
let tempcred = JSON.parse(req.headers.data)
let credentials = tempcred.credentials;
let userData = await utils.isValidUser((credentials), false);
if (userData.isValid == false) {
cb(null, false)
return;
}
else {
cb(null, true)
}
}
Then, since Multer uploads the file with a random name, and I need to save it with the user ID name and the file extension, I do the following:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/eofiles/')
},
filename: async function (req, file, cb) {
let tempcred = JSON.parse(req.headers.data)
let credentials = tempcred.credentials;
let userid = await utils.decrypt(credentials.userid, process.env.SECRET);
cb(null, userid + path.extname(file.originalname))
}
});
Finally, I declare the upload variable for using it with Multer:
var upload = multer({ storage: storage, fileFilter: authenticateUser })
And set the router:
router.post('/updateeofile', upload.single("myfile"), async (req, res, next) => {
let filename = req.file.filename //gets the file name
...
...
do my stuff, save on database, etc
...
...
});
For the record, "myfile" is the input file id.
And this is how I upload the file from Angular:
var APIURL = sessionStorage.getItem('endPoint') + "eoset/updateeofile";
const httpOptions = {
headers: new HttpHeaders({
'data': `${JSON.stringify(this.payLoad)}`
})
};
let formData = new FormData();
if (this.eofile) {
formData.append("myfile", this.eofile, this.eofile.name);
}
this.httpClient.post(APIURL, formData, httpOptions).subscribe(
result => {
...
...
...
},
error => {
});
I spent 6 hours on this today. I hope this helps you and saves you some time.
Try using Multer
npm i multer
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
router.post('/updateeocoverage', upload.single('myfile'), function (req, res, next) {
res.json(JSON.parse(req.body.data))
})
You will need to indicate the destination folder (line 2) and work out the file name, extension, etc.
First thing to do is ensure that the body isn't empty before making the request, so do a console.log before the request:
console.log(formData);
this.httpClient.post(APIURL, formData).subscribe(
result => {
....
After that, if the body is filled correctly, try to put the body like this on the request method:
this.httpClient.post(APIURL, {formData})
or
this.httpClient.post(APIURL, {
"field1": formData.field1,
...
})
and if these two things don't correct your issue, problaby you have something wrong on back-end side
Could you please help me to display uploaded image. For uploading i'm using multer. and then i need to display it in another route.
So, in folder 'uploads' every new file is on the head. Therefore I need to display it. How can i do this?
router.post('/upload', (request, response) => {
upload(request, response, (error) => {
if(error) {
request.flash('error_message', 'Only images are allowed')
response.redirect('/')
}
else {
if(request.file == undefined) {
request.flash('error_message', 'Image file was not been selected.')
response.redirect('/')
console.log(request.file)
}
else {
request.flash('success_message', 'Image was uploaded successfully.')
response.redirect('/compress')
console.log(request.file)
}
}
})
})
router.get('/compress', (request, response) => {
response.render('compress', {
//here i want to display it, but i didn’t succeed
})
})
after uploading file :
response.redirect(`/compress/${request.file.filename}`);
example : /compress/my-image.png
you need to create route like this :
var fs = require('fs');
var path = require('path');
router.get('/compress/:filename', (request, response) => {
let fileName = request.params.filename;
// setup your path as per your project folder
let pathToCheck= path.join(__dirname, '../../uploads/' +fileName);
console.log('pathToCheck==',pathToCheck);
// __dirname : will give you current directory path
// checking if file exists or not
if (fs.existsSync(pathToCheck)) {
// file found , display it
let imageUrl = `${request.protocol}://${request.headers.host}/uploads/${fileName}`;
response.render('compress', { imageUrl : imageUrl });
}else{
// file not found , render error 404 page
response.render('error404');
}
})
Hey everyone so quick question I want to allow a user to upload a WebM file and convert it using FFmpeg to mp4. I am using Nodejs for the backend and already have a route that uploads files to Amazon S3 file storage. But let's say I wanted to send that file and not store it anywhere but convert it to mp4 from the request itself is that possible? If not is it possible to take an s3 file URL and convert it to mp4? Can anybody point me in the right direction as to what is possible and the best way to do this?
basically all I want to do is
const objectUrl = createObjectURL(Blob);
ffmpeg -i objectURL S3OutputLocation
or
ffmpeg -i myS3InputLocation myS3OutputLocation
Okay so there is a couple of things you have to do in order to make this work.
1. you need to set up a local instance of multer as you need to upload the file locally before going to s3. I tried to do it with s3 directly but it seemed to use a lot of costly and time-consuming read operations that took much longer than writing the file to the server first. I found this to be the best solution.
You do this like so:
const localStorage = multer.diskStorage({
destination: function(req, file, cb) {
const destination = __dirname + "\\canvas-uploads";
console.log("destination", destination);
cb(null, destination);
},
filename: function(req, file, cb) {
const filename = req.body.id + "." + file.mimetype.toString().slice(file.mimetype.toString().lastIndexOf("/") + 1);
console.log("filename", filename);
cb(null, filename);
}
});
const uploadLocal = multer({
storage: localStorage
});
You need to set up ffmpeg-fluent and wrap it in a promise so you can be sure it's finished with all your processing (uploading to s3 and etc in the same route.) for convenience.
you do this like so:
router.post('/upload-temp', uploadLocal.array("upload"), async(req, res, next) =>{
res.json({id: req.body.id});
});
router.post('/ffmpeg', async(req, res, next) => {
try {
const reqPath = path.join(__dirname, '../upload/canvas-uploads/');
const {id, type} = req.body;
const localFileInput = `${reqPath}${id}.webm`;
const localFileOutput = `${reqPath}${id}.${type}`;
console.log("localInput", localFileInput);
console.log("localOutput", localFileOutput);
const key = `canvas/${id}.${type}`;
await new Promise((resolve, reject) => {
ffmpeg().input(localFileInput)
.withOutputFormat(type)
.output(localFileOutput)
.on('end',async ()=> {
const fileContent = await fs.readFileSync(localFileOutput);
await fs.unlinkSync(localFileInput);
await fs.unlinkSync(localFileOutput);
const params = {
Bucket: process.env.BUCKET_NAME,
Key: key,
Body: fileContent
}
await s3.putObject(params).promise();
resolve();
}).run();
})
res.send("success")
} catch(error){
console.log(error);
res.send(error);
}
});
I'm using Express to build a web API. In the following example, SVG data is converted to PNG and uploaded to S3.
const svg2png = require("svg2png");
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
app.post('/svg_to_png', function (req, res) {
let params = req.body
// STEP 1: Convert SVG to PNG:
var outputBuffer = svg2png.sync(params.svg_data, {});
// STEP 2: Upload to S3:
let s3_params = {
Bucket:params.bucket,
Key:params.key,
Body:outputBuffer,
ContentType:'image/png',
ContentDisposition:'inline',
ACL: 'public-read'
}
result = s3.putObject(s3_params,function(err,data){
if (err){
return err;
}
return 'success';
});
// Return Image URL:
let image_url = 'https://s3.amazonaws.com/' + params.bucket + '/' + params.key
res.send(image_url)
})
I want the API to respond with the URL of the converted image, which the requesting client can then immediately download. The problem is, the S3 upload operation is async, and so when the response is delivered, the image does not yet exist at the URL location, forcing the client to poll for its existence.
Is there a way to get the web server to respond only once the S3 upload has completed?
What about something like this :
const putObjPromise = s3.putObject(params).promise();
putObjPromise
.then(data => {
// Return the URL here.
})
.catch(err => console.log(err))
AWS has this doc for Promises : https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
Hope this helps.
as #Brandon mentioned, you can return the response once the s3 callback is completed. You can also use s3.putObject(params).promise(). I prefer this since it improves readability.
app.post('/svg_to_png', async function (req, res) {
let params = req.body
...
// STEP 2: Upload to S3:
let params = {
...
}
try {
const result = await s3.putObject(params).promise();
// Return Image URL:
// image_url = "https://s3.amazonaws.com/' + params.bucket + '/' + params.key
// res.body(....).end()
} catch(err) {
// return error response
}
})