EBUSY error when deleting a file using unlink - node.js

I have a delete route which deletes an image file. Following is the code:
router.delete('/:id', (req, res) => {
let pathForThumb = '';
let pathForImage = '';
Image.findOne({ _id: req.params.id })
.then(getImage => {
pathForThumb = getImage.thumbPath;
pathForImage = getImage.imagePath;
getImage.remove();
})
.then(removeThumb => {
fs.unlink(pathForThumb, (err) => {
if (err) {
req.flash('error_msg', 'There was an error deleting the thumbnail');
res.redirect('/user/userdashboard');
}
});
})
.then(removeMainImage => {
fs.unlink(pathForImage, (err) => {
if (err) {
console.log(err);
req.flash('error_msg', 'There was an error deleting the main image');
res.redirect('/user/userdashboard');
} else {
req.flash('success_msg', 'Image removed');
res.redirect('/user/userdashboard');
}
});
})
.catch(err => {
console.log(err);
});
});
as you can see when I upload a file I store it's path and also generate a thumbnail in the /uploads/thumbs/ folder and store the path of the thumb nail as well. In the above code I first get the image using findOne, store the paths of both images in variables and then call fs.unlink in a promise. What is happening is that my thumbnail gets deleted but I am getting the following error in the removeMainImage then condition:
{ Error: EBUSY: resource busy or locked, unlink 'C:\Users\Amin Baig\Desktop\Teaching\galleryprj\public\uploads\XC6kPqWf9_dphaBmUG__I7SN7PAEl_1531823330941_CEI21.jpg'
errno: -4082,
code: 'EBUSY',
syscall: 'unlink',
path: 'C:\\Users\\Amin Baig\\Desktop\\Teaching\\galleryprj\\public\\uploads\\XC6kPqWf9_dphaBmUG__I7SN7PAEl_1531823330941_CEI21.jpg' }
I am using windows 10 for my dev environment os.
Have been trying to find a solution for this, please help.

In my experience, Windows behaves unpredictable when it comes to file locks. In my projects I fixed errors like this by retrying until it works.
Here's some example code:
/**
* On windows, the file lock behaves unpredictable. Often it claims a databsae file is locked / busy, although
* the file stream is already closed.
*
* The only way to handle this reliably is to retry deletion until it works.
*/
const deleteFile = async (filePath) => {
try {
await unlink(filePath);
} catch (err) {
unlinkRetries--;
if (unlinkRetries > 0) {
await deleteFile();
} else {
throw err;
}
}
}

Related

fs-extra not removing files inside directory

I'm using fs-extra library to delete some image files on post request in my node js app. every time I call /deleteproduct route everything works fine. my product is removed from database and fs-extra callback doesn't throw any error even though files are not removed! I don't know what is the reason. I think maybe I'm doing something wrong with async/await functions.
this is my code:
router.post('/deleteproduct', async (req, res) => {
try {
const id = req.body.id;
const deleteProduct = await prisma.product.findUnique({
where: { id: id }
});
const images = JSON.parse(deleteProduct.image);
for(let i = 0; i < images.length; i++) {
await fsExtra.remove(path.join(__dirname, `public/images/${images[i]}`), (err) => {
if (err) console.log(err);
});
console.log(images[i]);
}
await prisma.product.delete({
where: { id: id }
});
res.status(200).json({ msg: "Deleted product with id: " + id });
} catch (error) {
res.json({ msg: error });
}
});
EDIT: image files are inside images folder in public directory.
directory image
please comment if you need more info
directories image:
directories image
cpanel.js is deleting the files
There might be two problems here. First, your are not using correct path to reference your files correctly. Secondly, you are using await and callbacks at the same time. You can do something like this.
try {
const images = JSON.parse(deleteProduct.image);
const imageProm = [];
for(let i = 0; i < images.length; i++) {
imageProm.push(fsExtra.remove(path.join(__dirname, `public/images/${images[i]}`)
}
const result = await Promise.all(imageProm);
await prisma.product.delete({
where: { id: id }
});
}
catch (e) {console.log(e)}
If the above solution doesn't work why can't you use fs.unlink a native method provided to work for such scenarios. Try using that.
Note: whenever you use async/await use try/catch block to catch errors.
Instead of this:
await fsExtra.remove(path.join(__dirname, `public/images/${images[i]}`), (err) => {
if (err) console.log(err);
});
Could you directly try this:
await fsExtra.remove(path.join(__dirname, `public/images/${images[i]}`));
fs-extra returns a promise so this should work. Adding a try/catch to check for errors can also be implemented

"TypeError: Cannot read property 'url' of undefined", and wrong command behavior. - discord.js

I've been working on a "artwall" bot with, basically write to .json file command. I want for the bot to get the first message attachment, get it's url and save it to save.json.
If there's a an attachment present, everything works fine, but if the command was initiated with a url or without any arguments at all, it gives this error:
TypeError: Cannot read property 'url' of undefined
Here's the command code:
const fs = require('fs');
// Export code for command.
module.exports = {
// In name type name of this command to execute it.
name: 'done',
// In description type description.
description: 'N/A',
// In execute() {...} circle brackets type execution parameters.
execute(client, message, args) {
// Type command code here.
const safety = JSON.parse(fs.readFileSync('./assets/save.json', 'utf8'));
const currentdeg = JSON.parse(fs.readFileSync('./assets/currentDEBUG.json', 'utf8'));
// const attache = JSON.parse(fs.readFileSync('./assets/attach.json', 'utf8'));
if(safety == 'no') {
if(currentdeg == 'not claimed') {
message.channel.send('The wall is not claimed yet. Claim it by using `wol claim`');
}
else if(currentdeg == message.author.id) {
const Attachment = (message.attachments).array();
console.log(Attachment);
if (Attachment == []) {
if (!args) {
message.reply('there\'s no image present. Make sure you attached one message or used url.');
return;
}
else {
fs.writeFile('assets/currentDEBUG.json', JSON.stringify('not claimed', null, 2), err => {
// Checking for errors
if (err) throw err;
console.log('Done writing (claim)');
});
fs.writeFile('assets/attach.json', JSON.stringify(args[0], null, 2), err => {
// Checking for errors
if (err) throw err;
console.log('Done writing (attache)');
});
}
}
// stuff
fs.writeFile('assets/currentDEBUG.json', JSON.stringify('not claimed', null, 2), err => {
// Checking for errors
if (err) throw err;
console.log('Done writing (claim)');
});
fs.writeFile('assets/attach.json', JSON.stringify(Attachment[0].url, null, 2), err => {
// Checking for errors
if (err) throw err;
console.log('Done writing (attache)');
});
}
else {
// stuff
message.channel.send('The artwall was claimed by someone else already. Wait for them to finish their work.');
}
}
else {
message.channel.send('The artwall is locked right now. Please wait for the next event!');
}
},
};
Thanks in advance!

Cant load protobuf message with protobuf.js

Im trying to use proto messages with protobuf.js, encode them and send them to a RabbitMQ message broker. I have the following sub-folders inside my project:
- model
- protos
- transactions.proto
- RabitMQ.js
- routes
- rmq-api.js
I added a route which does the following(Using express) in the rmq-api.js file:
const RabbitMQ = require('../model/RabbitMQ');
router.post('/api/transactions' ,function (req,res,next) {
RabbitMQ.PublishTransactionsMessage(DummyMessage).then(() => {
res.status(200).send({message: "OK :)"});
}).catch((e) => {
res.status(500).send({error:e.message});
});
});
In the RabitMQ.js file I have the following code:
module.exports = {
PublishTransactionsMessage: function(message) {
return new Promise((resolve, reject) => {
amqp.connect(RabbitMQConfig.url, function (error, connection) {
if (error) {
console.error("Could not connect to the rabbit message broker on {0} - " +
"Check connection please and try again".format(RabbitMQConfig.url));
console.error("Error message - {0}".format(error));
reject(error)
}
connection.createChannel(function(error, channel) {
if (error) {
console.error("Could Create channel - {0}".format(error.message));
reject(error)
}
const queue = RabbitMQConfig.queue;
channel.assertQueue(queue, {
durable: true
});
// Convert Message to protobuff
protobuf.load("./protos/transactions.proto").then((err, root) => {
if (err) {
reject(err);
}
let ScraperMessageResult = root.lookupType("transactions.ScraperMessageResult");
const errMsg = ScraperMessageResult.verify(message);
if (errMsg)
reject(errMsg);
let buffer = ScraperMessageResult.encode(message).finish();
channel.sendToQueue(queue, buffer);
console.log(`Sent ${message} to queue: ${queue}`);
resolve()
}).catch((err) => {
reject(err);
});
});
});
});
},
};
In the code shown above in the line:
protobuf.load("./protos/transactions.proto").then((err, root) => {
I keep catching the following error:
Inside this catch block:
}).catch((err) => {
reject(err);
});
This seems like a pretty simple problem however I havent found anything on this online so I might be missing something really simple here.
P.S. I tried using __dirname + "/protos/transaction.proto" and still couldnt get this to work.
Please help me figure this out.
The problem is that your function doesn't look at the dist directory,
await protocolBuffer.load(__dirname + '/item.proto'); should look there,
and also you need to copy the file manuly by using copy/cp command, you can add it as part of the package json script like so:
"build": "nest build && COPY src\\reading_list\\protocol_buffer\\*.proto dist\\reading_list\\protocol_buffer\\"

Sequelize transaction always returning null

I'm writing the backend for creating audit protocols. The user should be able to create criterias for the audit protocol. For this, i have the following backend-method to make sure, the protocol gets only created completely or the process of creating is canceled. It is possible to set several kinds of forms / criterias. But it could be, that only one kind of form is required. I do check that with the if-statement.
The creating works as expected. But the REST API always returns null to the clients. So i can't do further processing on the frontend regarding to the result of the creation process.
Technologies: Node.js and Sequelize. Frontend in angular / ionic. Database in mySQL.
I tried around with some transaction passing and return statements. I tried to compare it to a similiar code snippet, which works as expected.
exports.setAudit = (req, res, next) => {
trueFalseCriteria = req.body.trueFalseForms;
isShouldCriteria = req.body.isShouldForms;
generalCriteria = req.body.generalForms;
measurementCriteria = req.body.measurementForms;
toolId = req.body.toolId;
// Transaction is used to roll the whole transaction back if something wents wrong
return sequelize
.transaction(t => {
return audit
.create(
{
// Creating an audit referencing the tool
toolId: toolId
},
{ transaction: t }
)
.then(
// Getting the id of the audit that we just created
audit => {
return audit.id;
},
{ transaction: t }
)
.then(auditId => {
// Check wether the kind of form is used or not. If so, sequelize tries to do a bulk insert into the databases.
// Each bulk insert throws an error if it fails to cancel the whole transaction
if (trueFalseCriteria) {
console.log(1);
trueFalseCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
trueFalseCriterion.bulkCreate(trueFalseCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
if (isShouldCriteria) {
console.log(2);
isShouldCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
isShouldCriterion.bulkCreate(isShouldCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
if (generalCriteria) {
console.log(3);
generalCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
generalCriterion.bulkCreate(generalCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
if (measurementCriteria) {
console.log(4);
measurementCriteria.forEach(dataEl => {
dataEl.auditId = auditId;
});
measurementCriterion.bulkCreate(measurementCriteria).catch(err => {
// Throw error to cancel transaction
throw new Error(err);
});
}
}, { transaction: t });
})
.then(data => {
console.log(5);
res.status(200).json(data);
})
.catch(err => {
console.log(6);
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
};
Expected result: Http response with status code 200 on success
Actual result: null
I think you are missing a return for the last .then():
.then(auditId => {
// Check wether the kind of form is used or not. If so, sequelize tries to do a bulk insert into the databases.
.....
if (measurementCriteria) {
....
}
// RETURN SOMETHING HERE
}, { transaction: t });

Getting error AWS Lambda : EROFS: read-only file system, open '/var/task/assets/docs.zip'

Can any one help me that why i got this issue I run this code locally it runs perfectly but at aws lambda i got this error even i increase the time over lambda time out function as well memory.
In this code i do a basic task for get call i just convert a xlsx to json and in post i just convert a test dir to zip file.I tried it from last few hrs for uploading at aws lambda now I am stuck and seeing continously this error can anyone help me out from this situation thanks in advance.
here is my code
index.js
"use strict"
const fs = require("fs");
const path = require("path");
const ctrlFuns = require("./functionality");
const output = fs.createWriteStream(path.join(__dirname,
"./assets/docs.zip"));
const archiver = require("archiver");
const zipArchive = archiver("zip", {
gzip: true,
zlib: {
level: 9
} // Sets the compression level.
});
exports.handleHttpRequest = function (event, context, callback) {
if (event.http_method == "GET") {
ctrlFuns.xlsxToJson().then((jsonObjs) => {
callback(null, {
users: jsonObjs,
});
}).catch((err) => {
callback(err);
});
}
else if (event.http_method == "POST") {
fs.readFile(path.join(__dirname + "/test/test.xlsx"), (err, file) => {
if (err) {
callback(err);
} else {
//pipe archive data to the file
zipArchive.pipe(output);
zipArchive.append(file, {
name: "test.xlsx",
prefix: "test-data" //used for folder name in zip file
});
// to catch this error explicitly
zipArchive.on("error", (err) => {
callback(err);
});
//to perform end tasks while zip converted
zipArchive.on("end", () => {
fs.readFile(path.join(__dirname + "/assets/docs.zip"), (err,
success) => {
if (err) {
callback(err);
} else {
callback(null, success.toString("base64"));
}
});
});
//filnalizing the zip file for user use
zipArchive.finalize();
}
});
}
else {
callback(null, "run default case");
}
} //handler-closes
here is my functionality.js
/**
* OBJECTIVE: TO CREATE THE FUNCTINALITY
*/
"use strict"
const XLSX = require("xlsx");
const fs = require("fs");
const path = require("path");
var ctrlFuns = {};
ctrlFuns.xlsxToJson = function () {
return new Promise((resolve, reject) => {
fs.readFile(path.join(__dirname + "/test/test.xlsx"), (err, file) => {
if (err) {
reject(err);
} else {
let workbook = XLSX.read(file.buffer, {
type: "buffer"
});
//if workbook is null
if (!workbook) {
reject("Workbook not found.");
}
/* Getting first workbook sheetName */
let first_sheet_name = workbook.SheetNames[0];
/* Get worksheet */
let worksheet = workbook.Sheets[first_sheet_name];
/**Convert Into JSON */
resolve(XLSX.utils.sheet_to_json(worksheet, {
raw: true
}));
}
});
})
} //fun-closes
module.exports = ctrlFuns;
when I saw the logs at cloud watch then i got:
START RequestId: 720cf48f-01c4-11e9-b715-9d54f664a1e8 Version: $LATEST
2018-12-17T06:24:45.756Z 720cf48f-01c4-11e9-b715-9d54f664a1e8 Error: EROFS: read-only file system, open '/var/task/assets/docs.zip'
END RequestId: 720cf48f-01c4-11e9-b715-9d54f664a1e8
with below error message:
{
"errorMessage": "RequestId: 98b9e509-01c7-11e9-94dc-03cfdf0dae93 Process exited before completing request"
}
The error seems self-explanatory:
Error: EROFS: read-only file system, open '/var/task/assets/docs.zip'
/var/task is where your Lambda function code is located, and in the actual Lambda environment, that filesystem is read-only. If you need to write to a file, you need to write to /tmp.
Q: What if I need scratch space on disk for my AWS Lambda function?
Each Lambda function receives 500MB of non-persistent disk space in its own /tmp directory.
https://aws.amazon.com/lambda/faqs/
Note that you also need to clean up after yourself and remove any temporary files you created, because once a function finishes executing, its container is available for reuse by a later invocation of the same function... which means this same temp space may persist for a short time and be seen again (but only by this same function).

Resources