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');
}
})
Related
hope you can help me on this one!
Here is the situation: I want to download a file from my React front end by sending a request to a certain endpoint on my express back end.
Here is my controller for this route.
I build a query, parse the results to generate a csv file and send back that file.
When I console log the response on the front end side, the data is there, it goes through; however, no dialog open allowing the client to download the file on local disk.
module.exports.downloadFile = async (req, res) => {
const sql = await buildQuery(req.query, 'members', connection)
// Select the wanted data from the database
connection.query(sql, (err, results, fields) => {
if (err) throw err;
// Convert the json into csv
try{
const csv = parse(results);
// Save the file on server
fs.writeFileSync(__dirname + '/export.csv', csv)
res.setHeader('Content-disposition', 'attachment; filename=export.csv');
res.download(__dirname + '/export.csv');
} catch (err){
console.error(err)
}
// Reply with the csv file
// Delete the file
})
}
Follow one of these functions as an example to your client side code:
Async:
export const download = (url, filename) => {
fetch(url, {
mode: 'no-cors'
/*
* ALTERNATIVE MODE {
mode: 'cors'
}
*
*/
}).then((transfer) => {
return transfer.blob(); // RETURN DATA TRANSFERED AS BLOB
}).then((bytes) => {
let elm = document.createElement('a'); // CREATE A LINK ELEMENT IN DOM
elm.href = URL.createObjectURL(bytes); // SET LINK ELEMENTS CONTENTS
elm.setAttribute('download', filename); // SET ELEMENT CREATED 'ATTRIBUTE' TO DOWNLOAD, FILENAME PARAM AUTOMATICALLY
elm.click(); // TRIGGER ELEMENT TO DOWNLOAD
elm.remove();
}).catch((error) => {
console.log(error); // OUTPUT ERRORS, SUCH AS CORS WHEN TESTING NON LOCALLY
})
}
Sync:
export const download = async (url, filename) => {
let response = await fetch(url, {
mode: 'no-cors'
/*
* ALTERNATIVE MODE {
mode: 'cors'
}
*
*/
});
try {
let data = await response.blob();
let elm = document.createElement('a'); // CREATE A LINK ELEMENT IN DOM
elm.href = URL.createObjectURL(data); // SET LINK ELEMENTS CONTENTS
elm.setAttribute('download', filename); // SET ELEMENT CREATED 'ATTRIBUTE' TO DOWNLOAD, FILENAME PARAM AUTOMATICALLY
elm.click(); // TRIGGER ELEMENT TO DOWNLOAD
elm.remove();
}
catch(err) {
console.log(err);
}
}
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'));
I need some help on an issue. I try to download a file on my client ReactJs from my server Nodejs.
I've got into my server.js :
router.route("/download/:filesaveas").get(function(req, res) {
const fileLocation = "public/files/" + req.params.filesaveas;
const file = req.params.filesaveas;
res.download(fileLocation, file, (err) => {
if (err) console.log(err);
});
When I try to download directly from the server http://localhost:4000/cww/download/testfile.pdf, the download works, I don't have any error and the file is not corrupted.
On my client side, I've got a function downloadFile which is called by a button "onclick" action.
import download from 'downloadjs'
downloadFile = (filetodownload) => {
axios.get('http://localhost:4000/cww/download/'+filetodownload)
.then(res => {
var filename = "testfile.pdf"
download(res.data, scriptname, "text/plain");
});
}
When I click on the button. Something is downloaded but the file seems to be corrupted. Impossible to open... I think, I've got a problem with the response data from the server.
By doing a console.log(res.data), I can see a part of my content PDF but with some strange
characters (like encoding) but impossible to have a correct file downloaded.
Please thanks for your help.
if you want easiest option would be to open a new tab with that file's address which works only if route is public.
const newTab = false;
window.open('http://localhost:4000/cww/download/testfile.pdf',newTab ? '' : '_self' );
but you can do it without touching the file encoding or even use axios for this:
onClick() {
const fileName = 'testfile.pdf';
fetch('http://localhost:4000/cww/download/testfile.pdf', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
// Security Headers if needed
},
body: undefined,
})
.then((data) => {
return data.blob();
})
.then((data) => {
if (data.size === 0) {
throw new Error('File not found');
}
const fileURL = URL.createObjectURL(data);
const downloadLink = document.createElement('a');
downloadLink.href = fileURL;
downloadLink.download = fileName;
downloadLink.click();
})
.catch((err) => {
console.log(err);
// Error Action
});
}
Backend will simply stream the file to user.
I don't know much about res.download (might be better solution)
import * as fs from 'fs';
export async function getFile(request, response) {
const fileStream = fs.createReadStream('yourFileAddress', {});
fileStream.on('error', (err) => {
console.log(err.message);
response.status(404).send();
fileStream.removeAllListeners();
});
fileStream.on('end', () => {
console.log('Streamed successfully to user');
fileStream.removeAllListeners();
});
// finally starting the stream
fileStream.pipe(response);
}
Thanks for your help ! I've just found my error !
I forgot to add ReponseType: blob and now it works perfectly ;-)
downloadFile = (filetodownload) => {
axios.get('http://localhost:4000/cww/download/'+filetodownload, {responseType: 'blob'})
.then(res => {
var filename = "testfile.pdf"
download(res.data, scriptname, "text/plain");
});
}
I'm using formidable to parse incoming files and store them on AWS S3
When I was debugging the code I found out that formidable is first saving it to disk at /var/folders/ and overtime some unnecessary files are stacked up on disk which could lead to a big problem later.
It's very silly of me using a code without fully understanding it and now
I have to figure out how to either remove the parsed file after saving it to S3 or save it to s3 without storing it in disk.
But the question is how do I do it?
I would appreciate if someone could point me in the right direction
this is how i handle the files:
import formidable, { Files, Fields } from 'formidable';
const form = new formidable.IncomingForm();
form.parse(req, async (err: any, fields: Fields, files: Files) => {
let uploadUrl = await util
.uploadToS3({
file: files.uploadFile,
pathName: 'myPathName/inS3',
fileKeyName: 'file',
})
.catch((err) => console.log('S3 error =>', err));
}
This is how i solved this problem:
When I parse incoming form-multipart data I have access to all the details of the files. Because it's already parsed and saved to local disk on the server/my computer. So using the path variable given to me by formidable I unlink/remove that file using node's built-in fs.unlink function. Of course I remove the file after saving it to AWS S3.
This is the code:
import fs from 'fs';
import formidable, { Files, Fields } from 'formidable';
const form = new formidable.IncomingForm();
form.multiples = true;
form.parse(req, async (err: any, fields: Fields, files: Files) => {
const pathArray = [];
try {
const s3Url = await util.uploadToS3(files);
// do something with the s3Url
pathArray.push(files.uploadFileName.path);
} catch(error) {
console.log(error)
} finally {
pathArray.forEach((element: string) => {
fs.unlink(element, (err: any) => {
if (err) console.error('error:',err);
});
});
}
})
I also found a solution which you can take a look at here but due to the architecture if found it slightly hard to implement without changing my original code (or let's just say I didn't fully understand the given implementation)
I think i found it. According to the docs see options.fileWriteStreamHandler, "you need to have a function that will return an instance of a Writable stream that will receive the uploaded file data. With this option, you can have any custom behavior regarding where the uploaded file data will be streamed for. If you are looking to write the file uploaded in other types of cloud storages (AWS S3, Azure blob storage, Google cloud storage) or private file storage, this is the option you're looking for. When this option is defined the default behavior of writing the file in the host machine file system is lost."
const form = formidable({
fileWriteStreamHandler: someFunction,
});
EDIT: My whole code
import formidable from "formidable";
import { Writable } from "stream";
import { Buffer } from "buffer";
import { v4 as uuidv4 } from "uuid";
export const config = {
api: {
bodyParser: false,
},
};
const formidableConfig = {
keepExtensions: true,
maxFileSize: 10_000_000,
maxFieldsSize: 10_000_000,
maxFields: 2,
allowEmptyFiles: false,
multiples: false,
};
// promisify formidable
function formidablePromise(req, opts) {
return new Promise((accept, reject) => {
const form = formidable(opts);
form.parse(req, (err, fields, files) => {
if (err) {
return reject(err);
}
return accept({ fields, files });
});
});
}
const fileConsumer = (acc) => {
const writable = new Writable({
write: (chunk, _enc, next) => {
acc.push(chunk);
next();
},
});
return writable;
};
// inside the handler
export default async function handler(req, res) {
const token = uuidv4();
try {
const chunks = [];
const { fields, files } = await formidablePromise(req, {
...formidableConfig,
// consume this, otherwise formidable tries to save the file to disk
fileWriteStreamHandler: () => fileConsumer(chunks),
});
// do something with the files
const contents = Buffer.concat(chunks);
const bucketRef = storage.bucket("your bucket");
const file = bucketRef.file(files.mediaFile.originalFilename);
await file
.save(contents, {
public: true,
metadata: {
contentType: files.mediaFile.mimetype,
metadata: { firebaseStorageDownloadTokens: token },
},
})
.then(() => {
file.getMetadata().then((data) => {
const fileName = data[0].name;
const media_path = `https://firebasestorage.googleapis.com/v0/b/${bucketRef?.id}/o/${fileName}?alt=media&token=${token}`;
console.log("File link", media_path);
});
});
} catch (e) {
// handle errors
console.log("ERR PREJ ...", e);
}
}
I have a requirement where I want to upload multiple images in node.js. so I am using angular2 for frontend. I am using formData to send the files to node part.
below is the code :
public setAnswerImage(obj:any,files:any):Promise<any> {
try {
let urlDetails = obj["id"] ? ANSWER_URLS['updateimages'] : ANSWER_URLS['insertimages'];
let formData = new FormData();
Object.keys(obj).forEach(key =>{
formData.append(key,obj[key]);
});
console.log(formData)
for(let i =0; i < files.length; i++){
formData.append("uploads[]", files[i],files[i]['name']);
}
console.log(formData)
return this.restUtils.post(urlDetails, formData, {"id":obj["id"]});
}
catch(err) {
return Promise.reject(err);
}
}
let urlDetails = obj["id"] ? ANSWER_URLS['updateimages'] : ANSWER_URLS['insertimages'];
If obj contains id I will be calling update method else insert method.
export const ANSWER_URLS = {
insertimages : { url : "/api/answer/response/insertimages"}
}
In Node part I am using multer file storage, i have created a folder as temp and path has been set to this folder in multer as shown below.
router.post("/response/insertimages", upload.array("uploads[]", 12), (req: Request, res: Response, next: NextFunction) => {
new AnswerRoute().saveAnswerImages(req, res, next);
});
var storage = multer.diskStorage({
destination: (req: Request, file, cb) => {
cb(null, video_path.dest + 'temp//');
}, filename: (req:Request, file, cb) => {
let name = file.originalname.split('.');
console.log(name,"mnae")
cb(null, new Date().getTime().toString() + '.' + name[name.length-1]);
}
})
var upload = multer({ storage: storage });
Console.log output shows multiple files I uploaded but in temp folder, I can see only one :
[ 'doctor', 'jpg' ] 'mnae'
[ 'Doctorvisits', 'png' ] 'mnae'
The above output shows two images I uploaded but only one file is saved in the temp folder.
I am using upload.array("uploads[]",12), where uploads contain multiple files but the problem is when I upload multiple files only first file gets uploaded to the temp folder.
Am I going wrong somewhere?
Please help me