I need to remove an image file from my backend, the folder is: /uploads. When i call the function deleteProduct it removes the product from the data base but the image of the product its still in folder.
deleteProduct: (req, res) => {
let productId = req.params.id;
Product.findById(productId, (err, res) =>{
var imageResponse = res.image;
console.log(imageResponse);
});
//console.log(imageResponse);
//fs.unlink('./uploads' + imageResponse );
When i try to access imageResponse outside the findById, console prints: "imageResponse" is not defined. Then i need to delete that file with fs. Im not sure if i wrote correct the unlink function. Thanks in advance.
For fs.unlink
Have you made sure to:
Include fs = require('fs')?
Used __dirname?
Include file extension (.png, .jpg, .jpeg)?
const fs = require('fs');
fs.unlink(__dirname + '/uploads' + imageResponse + ".png", (err) => {
if (err) throw err;
console.log('successfully deleted file');
});
For image response being undefined
You didn't provide information on the Product constructor, but I assume Product.findById is asynchronous. You may need to use an async function
const fs = require('fs');
async function deleteProduct (req, res) => {
let productId = req.params.id;
Product.findById(productId, (err, res) =>{
var imageResponse = res.image;
console.log(imageResponse);
fs.unlink(__dirname + '/uploads' + imageResponse + ".png", (err) => {
if (err) throw err;
console.log('successfully deleted file');
});
});
}
Further reading:
Node File API: https://nodejs.org/api/fs.html
Async functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Finally it seems to be working, the file succesfully vanished from the folder, im still open for advices, thanks.
deleteProduct: (req, res) => {
let productId = req.params.id;
Product.findById(productId, (err, res) =>{
if(err) return res.status(500).send({message: 'Error'});
fs.unlink('./uploads/' + res.image, (err) => {
if(err) return res.status(500).send({message: 'Error'});
})
});
Related
I'm making a text hosting service and I want it to say "TextMessage Not found! You can create one in the name by clicking here" but I don't know how to do that.
I tried making a custom 404 error, and detecting if the file exists via the node fs module, that did not work out.
this is my code:
// Importing require packages/modules
const express = require("express");
const bodyParser = require("body-parser");
const fs = require("fs");
// Defining needed variables
const app = express();
const PORT = 3000;
// Express midleware to enable body-parser
app.use(bodyParser.urlencoded({ extended: false }));
// Express static handler
app.use(express.static("public"));
// POST API To create message/file
app.post("/api/message/submit", (req, res) => {
const file = req.body.messageText;
const fileID = req.body.messageID;
fs.writeFile(__dirname + "/messageFiles/" + fileID + ".txt",
file, (err) => {
if (err) res.send("ERROR! <br>" + err);
else res.send("Saved");
});
});
// GET API To read message/file
app.get("/message/:id", (req, res) => {
const msg = req.params.id;
if (fs.existsSync(__dirname + "/messageFile/" + msg + ".txt")) {
res.sendFile(__dirname + "/messageFiles/" + msg + ".txt");
} else {
res.send("Message does not exist");
}
});
// Running the server
app.listen(PORT, () => {
console.log("Running on port: " + PORT);
});
`
You may try async writing to a file by wrapping it with trycatch block. It seems you have a problem with path to the file.
I suggest you to have a helper functions to write to a file and read from a file:
const create = async (data, fileName) => {
try {
const text = await fs.writeFile(`./files/${fileName}.txt`, data);
console.log('File write successful!');
} catch (error) {
throw new Error('File write operation failed');
}
};
const read = async (fileName) => {
try {
const fileContent = await fs.readFile(`./files/${fileName}.txt`, {encoding: 'utf-8'});
return fileContent;
} catch (error) {
throw new Error('File read operation failed');
}
};
app.post("/api/message/submit", async (req, res) => {
const fileContent = req.body.messageText;
const fileID = req.body.messageID;
const message = await create(fileContent, fileID);
message ? res.send('Successful') : res.send('Failed');
});
app.get("/message/:id", async (req, res) => {
const msgID = req.params.id;
const message = await read(msgID);
res.send(message);
});
I hope it helps!
P.S.: Don't use too many comments. Your code should be self-documenting (readable)!
P.S.S: Added async keyword before (req, res).
I'm making an ejs app where it returns a randomized gif from a database. I have a page that successfully confirms you uploaded a gif (images page.ejs) but in my home.ejs file where it renders from the /random path I get an error where it can't find the property of 'img' even though it's in my schema. Attached is a link to the GitHub page of a zip file of what I have so far. Link
`app. get('/random', async (req, res) =>{
const findGif = await gifFinder();
res.render('home', {findGif})
});
const gifFinder = async (req, res) => {
const foundGif = imgModel.count().exec(function (err, count) {
// Get a random entry
const random = Math.floor(Math.random() * count)
// Again query all users but only fetch one offset by our random #
imgModel.findOne().skip(random).exec(
function (err, result) {
// Tada! random user
// console.log(result)
})
});
//return the found gif
return foundGif;
};`
findOne already returns a random element from the collection:
app.get('/random', async (req, res) => {
try {
const findGif = await imgModel.findOne();
res.render('home', { findGif });
} catch (err) {
console.log(err);
}
});
I am trying to serve a static directory dynamically by adding a pathname parameter to the URL.
I am able to serve the directory just fine with the following line, which renders the html and subdirectories in the browser without having to readFile etc:
app.use('/', express.static('/Users/virtuload-beta/backend/uploads/folder/subfolder/'))
This helped for testing but I need it to be dynamic as I am getting the directory name depending on a path variable from MongoDB, and then serving the directory based on the URL.
I've tried multiple solutions, this is my current one:
app.js:
app.use('/static', express.static(path.join(__dirname, '../uploads', )), serveRouter)
routes.js:
router.get('/:id', FileCtrl.servepath);
-FileCtrl.js:
const servepath = async (req, res) => {
try {
let id = req.params.id
Upload.findById(id)
.populate('Upload')
.select('relPath') //relPath = /folder/subfolder
.exec(function(err, upload) {
if (err) {
res.send(err)
} else {
const filepath = `${upload.relPath}`
console.log(filepath) //logs folder/subfolder
//parse http object coming from client
const urlObject = url.parse(req.url, true)
console.log(urlObject)
var myUrl = new URL(`http://localhost:8000/static/${filepath}`)
return myUrl;
}
})
} catch (e) {
console.error(e)
}
}
I'm not getting any error but it's not working.
Manipulate the req.url and return next()
First your route
router.get('/:id', FileCtrl.servepath);
Controller(addednext):
const servepath = async (req, res, next) => {
try {
let id = req.params.id
Upload.findById(id)
.populate('Upload')
.select('relPath') //relPath = /folder/subfolder
.exec(function (err, upload) {
if (err) {
res.send(err)
} else {
const filepath = `${upload.relPath}`
req.url = `/static/${pathfile}/index.html`
return next();
}
})
} catch (e) {
console.error(e)
}
}
Last your static route (note: define it after all other routes)
app.use('/static', express.static(path.join(__dirname, '../uploads')), serveRouter)
Here's the problem.
I have a global variable(Array type) named folders
let folders = [];
I modify it inside a Callback function inside yet another callback function.
Here's how.
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
files.forEach(file => {
const add = folder => folders.push(folder);
fs.lstat(path.join(dir, file), (err, stats) => {
if (err) throw err;
if (stats.isDirectory()) {
add(file);
}
console.log("INSIDE: " + folders);
});
console.log("OUTSITE: " + folders);
});
});
res.send(folders.length.toString());
});
Now the problem is, that when I read it on this line:
res.send(folders.length.toString());
The length is always 0.
And it is also 0 on console log line where I Print it with OUTSITE but it reads fine when I print it on the line where I mention it with INSIDE.
I know the problem after some search. It happens because the callback sets the variable on a later time in the event loop.(If it makes any sense, but you get the point).
I know the problem but I don't have any idea of how to solve it. I have tried various implementations including adding a global function that pushes to the array and calling it frpm inside the callback but the results are same.
Here's the full code.
const express = require("express");
const fs = require("fs");
const path = require("path");
const os = require("os");
// Initialize Express
const app = express();
// PORT on which the app process should be started
const PORT = process.env.PORT || 5100;
// Setting Up the path to Projects folder dynamically
// !Currently only works(tested) on the Darwin(MacOS) systems PS. I don't own a Windows
// TODO: Test on Windowsn and Linux
const homedir = os.homedir();
const dir = `${homedir}/Projects/`;
// TODO: Re-Write using Async/Await as it is not fully suppported as of Node version 10.0
let folders = [];
// Home Route
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
files.forEach(file => {
const add = folder => folders.push(folder);
fs.lstat(path.join(dir, file), (err, stats) => {
if (err) throw err;
if (stats.isDirectory()) {
add(file);
}
console.log("INSIDE: " + folders);
});
console.log("OUTSITE: " + folders);
});
});
res.send(folders.length.toString());
});
// Start the express server
app.listen(PORT, err => {
if (err) throw err;
console.log(`Project Lister Running On PORT: ${PORT}`);
});
Any solutions?
The issue here is that fs.lstat is asynchronous.
If you use the sync version fs.lstatSync, then you can call res.send after the forEach loop.
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
files.forEach(file => {
const add = folder => folders.push(folder);
try {
const stats = fs.lstatSync(path.join(dir, file))
if (stats.isDirectory()) {
add(file);
}
} catch (err) {
throw err
}
});
res.send(folders.length.toString());
})
})
Or for a non-blocking way you could use Promise.all:
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
const promises = files.map(file => {
return new Promise((resolve, reject) => {
fs.lstat(path.join(dir, file), (err, stats) => {
if (err) {
reject(err);
}
if (stats.isDirectory()) {
add(file);
resolve();
}
console.log("INSIDE: " + folders);
});
});
});
Promise.all(promises, () => {
res.send(folders.length.toString());
});
});
});
So, here's the simplest solution I can find on my own!
#PeterN's answer is correct but could be hard to wrap a beginner's head around!
Here's my final code.
const express = require("express");
const fs = require("fs").promises; // !IMPORTANT Get the promises version of the File System Module
const path = require("path");
const os = require("os");
// Initialize Express
const app = express();
// PORT on which the app process should be started
const PORT = process.env.PORT || 5100;
// Setting Up the path to Projects folder dynamically
// !Currently only works(tested) on the Darwin(MacOS) systems PS. I don't own a Windows
// TODO: Test on Windows and Linux
const homedir = os.homedir();
const dir = `${homedir}/Projects/`;
// Home Route
app.get("/", async (req, res) => {
let folders = [];
// TODO: Proceed only if the path is correct and is a directory
try {
let files = await fs.readdir(dir);
for (let i = 0; i < files.length; i++) {
let file = await fs.lstat(path.join(dir, files[i]));
if (file.isDirectory()) {
folders.push(files[i]);
}
}
} catch (error) {
console.error(error);
}
res.send(folders);
});
// Start the express server
app.listen(PORT, err => {
if (err) throw err;
console.log(`Project Lister Running On PORT: ${PORT}`);
});
Take note, on the second line where I am importing the 'fs' modules, I now import it differently or rather say a different version!
I now import it as:
const fs = require("fs").promises;
The '.promises' added at the last imports the functions, methods of that module in their Promise based implementation. Thought you must note that it is stable only in version 11.x and up of NodeJs as of right now. I am using >12.x.
Now the rest of the process is rather straight forward assuming you are familiar with Async/Await and Promises. And if you're not I would highly suggest getting into it as it can save your day as it did with me.
Here's a great tutorial regarding it: Async/Await and Promise in JS
Ps. Use the for loop instead of 'array.forEach(e => //Do Something);' approach as it will again introduce the same problem as faced earlier because it is also callback-based!
Hope I helped you. Thanks!
I am working on a web application for an online library. I want to extract metadata from the PDF's that will be uploaded and for that I am using the nodejs library pdf.js-extract and multer-gridfs-storage for the upload. The problem is that I am receiving a PDF file (req.file) and the function requires a path or link to the PDF file and therefore shows the error
"TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be one of type string, Buffer, or URL. Received type object"
I would like to know if there is a way to pass a file as a link, save the file locally temporarily or find another library that fits my needs.
This is my current code.
const PDFExtract = require('pdf.js-extract').PDFExtract;
app.post('/upload', upload.single('file'), (req, res) => {
const pdfExtract = new PDFExtract();
const options = {};
pdfExtract.extract(req.file, options, (err, data) => {
if (err){
res.status(404).send({ message: err });
}
res.status(200).send({ message: data });
});
});
(Edit for clarification) I am using multer with gridFS to upload a file to mongoose.
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
Solution inspired by Oliver Nybo
app.post('/upload', upload.single('file'), (req, res) => {
const pdfExtract = new PDFExtract();
const options = {};
var readableStream = gfs.createReadStream({ filename : req.file.filename });
var buff;
var bufferArray = [];
readableStream.on('data',function(chunk){
bufferArray.push(chunk);
});
readableStream.on('end',function(){
var buffer = Buffer.concat(bufferArray);
buff=buffer;
pdfExtract.extractBuffer(buff, options, (err, data) => {
if (err) {
res.status(404).send({ message: err });
}
res.status(200).send({ message: data });
});
})
});
According to multer's api documentation, you can use req.file.path to get the full path of the uploaded file.
const PDFExtract = require('pdf.js-extract').PDFExtract;
app.post('/upload', upload.single('file'), (req, res) => {
const pdfExtract = new PDFExtract();
const options = {};
pdfExtract.extract(req.file.path, options, (err, data) => {
if (err){
res.status(404).send({ message: err });
}
res.status(200).send({ message: data });
});
});
Edit: I just read the multer options and there is an option called preservePath.
preservePath - Keep the full path of files instead of just the base name
Edit 2: I think you need to extract the file from the database with gridfs-stream, then convert it into a buffer (like in this thread), and then use PDFExtract's extractBuffer function.