fs.unlink doesn't delete the file - node.js

On my Express server I want to take the file uploaded by the user and rename it to match their username. If the username uploads a new file, the previous file is replaced.
Here's the code:
var newPath = 'uploads/' + user.username + '.' + (file.extension).toLowerCase();
var basePath = path.resolve(__dirname + '../../../') + '/';
// Copy, rename and delete temp file
var is = fs.createReadStream(basePath + file.path);
var os = fs.createWriteStream(basePath + newPath);
is.pipe(os);
is.on('end', function (error) {
if (err) return res.send(500);
fs.unlink(basePath + file.path);
});
Problem is that fs.unlink(basePath + file.path); doesn't actually delete the old file on my machine (OSX 10.9.2). How can i make sure the temp file is deleted?

The file basePath + file.path has link reference by the read stream is. The removal of the file contents shall be postponed until all references to the file are closed. You might want to call fs.unlink on the close event.

I just use fs.writeFile which overwrites a file. I have code in my app to this synchronously, but it looks like this:
if( !fs.existsSync( filename ) ) fs.openSync( filename, "wx" );
fs.writeFileSync( filename, data );
Basically I check if the file exists, and if it doesn't I open it. Then, I just write to the file. If it is new or already existing, only my data is present in the file when I look at it, overwriting what was there.

Related

How to find file extension change with node.js?

How do you detect a file extension change and save the new extension or full file path to a variable? This is a unique specific question.
Code I have so far dont mind if you dont use it:
const puppeteer = require('C:/Users/user1/Desktop/puppeteer_automation/node_modules/puppeteer');
(async () => {
var filename = "C:/Users/user1/Downloads/file.crdownload";
var downloadanduploadpath = "C:/Users/user1/Downloads";
var fs = require('fs');
var event1 = "change";
var currentstat = fs.stat();
const WATCH_TARGET = filename;
fs.watch(WATCH_TARGET, function(event1, downloadanduploadpath) {
console.log('File "' + filename + '" was changed: ' + eventType);
});
/*
fs.watch(downloadanduploadpath, (event1, filename) => {
console.log('event is: ' + event1);
if (filename) {
console.log('filename provided: ' + filename);
} else {
console.log('filename not provided');
}
});*/
//console.log(lastdownloadedimage);
})();
Error:
(node:9876) UnhandledPromiseRejectionWarning: TypeError
[ERR_INVALID_CALLBACK]: Callback must be a function. Received
undefined
As described in the docs,
The listener callback gets two arguments (eventType, filename). eventType is either 'rename' or 'change', and filename is the name of the file which triggered the event.
you can use the eventType argument to determine whether the filename has been changed.
UPDATE: I updated my example code to observe the directory instead of the file itself, in order to get the new filename in the filename argument
Example:
let fs = require('fs');
(async () => {
const WATCH_DIR = "C:/Users/user1/Downloads";
let target_file = "file.crdownload";
let renameTriggered = false;
fs.watch(WATCH_DIR, function(eventType, filename) {
if(eventType == 'rename') {
// Check if the target filename was changed (in the first event
// the old filename disappears, which marks the beginning of a renaming process)
if(filename == target_file) {
// Toggle renaming status
renameTriggered = true;
}
// The second event captures the new filename, which completes the process
else if(renameTriggered) {
// Toggle renaming status
renameTriggered = false;
console.log('File "' + target_file + '" was renamed to: "' + filename + '"');
// Update target filename
target_file = filename;
}
}
});
})();
Observing the directory however will trigger the callback on any filename changes that occur (includes even deleting and creating files). Therefore we check if the eventType is equal to 'rename'.
From the perspective of fs.watch() the renaming takes place in two steps: In the fisrt step the 'disappearing' of the old filename is detected which triggers the event passing the old filename as the argument filename. In the second step the 'appearance' of the new filename is detected which triggers the rename event again, this time with the new filename as the second argument, which is what we are looking for.
Your code example, is calling fs.watch() wrong. You don't pass arguments to the callback yourself. You just name parameters.
This program below all by itself works on Windows. I pass it a directory and watch for changes to any of the files in that directory. You can also pass it the filename for an existing file and watch for changes to just that file:
const fs = require('fs');
fs.watch("./watcherDir", function(eventType, filename) {
console.log(`eventType=${eventType}, filename=${filename}`);
});
eventType and filename are passed by fs.watch() to the callback, they are not something you pass. You just create the name of the arguments that you can then use as function arguments inside the callback.
When I rename a file in that directory from example.json to example.xxx, I get these two callbacks:
eventType=rename, filename=example.json
eventType=rename, filename=example.xxx
The filenames are, of course, relative to the directory I passed to fs.watch().
Note: As documented, this feature does not work the same on all platforms so for any further debugging we would need to know what platform you're running it on.

Switch to what ever path is input nodejs

Lets assume you have installed an electron app and you are asked to input the path to your current project. You might do something like: ~/Documents/projectName.
How do I, in node take that input and check if it exists, specifically if you entered in the path as shown above?
the reason for this is that I want to see if A) the path exists and B) if theres a specific file there (I'll be using path.join(dirEntered, fileName.extension).
Is there a way to do what I want? I see chdir but that changes where the working directory is. which I guess would be fine but doing:
process.chdir('~/Documents') Shows: no such file or directory, uv_chdir(…)
I want to avoid having the user to enter the full absolute path of their project. That seems "bad to me". And uploading their project isn't necessary, Im reading a single file (so theres no need for upload here).
Any ideas?
Is it possible to tap into the cli commands and take this input feed it there and get the result? Or is that over kill?
Here's an idea how to solve it. If the path starts with a tilde, it replaces that tilde with the full home directory of the current user. It then uses fs.stat to see if the given path actually exists.
const fs = require("fs");
const os = require("os");
var path = "~/Documents";
if (path.indexOf("~") === 0) {
path = os.homedir() + path.substring(1);
}
fs.stat(path, (err, stats) => {
if (!err) {
// document or path exists
if (stats.isFile()) {
console.log("Path " + path + " is a file");
} else if (stats.isDirectory()) {
console.log("Path " + path + " is a directory");
}
} else {
// document or path does not exist
}
});

fs.watch unexpected behavior

If I run the below program as node watcher.js file.txt, then it works as expected when I touch file.txt. But if I open file.txt in vim and save, then it ceases to detect future modifications to the file. This seems really weird to me, why does this behavior occur?
var fs = require('fs');
var args = process.argv;
if (args.length <= 2) {
console.log('USAGE: ' + args[1] + ' filename');
process.exit(1);
}
var filename = args[2];
fs.watch(filename, function(event, filename) {
console.log('file ' + filename + ' changed!');
});
It is important to inspect the content of the first argument, not just the filename. The issue is that event can be either 'change' OR 'rename'.
In this case, it looks like vim is actually renaming the old file and making a new one.

ZIP existing images using JSZip and NodeJS

I want to zip images using JSZip and NodeJS but it doesn't work, it works with simple file like .txt ... But with images it doesn't work and I don't know why...
My code :
var newFileName = pathDir + '/' + id + '.jpg';
fs.readFile(newFileName, function(err, data) {
zip.file(id+'.jpg', data, {base64: true});
});
Try:
var newFileName = pathDir + '/' + id + '.jpg';
var data = fs.readFileSync(newFileName);
zip.file(id+'.jpg', data, {base64: true});
In your case, you overwrite the id.jpg file of your zip instance using chunk data again and again...
// create a file
zip.file("hello.txt", "Hello[p my)6cxsw2q");
// oops, cat on keyboard. Fixing !
zip.file("hello.txt", "Hello World\n");
The content of hello.txt is "Hello World\n" rather than "Hello[p my)6cxsw2qHello World\n". Hope it helps.

Meteor/Node writeFile crashes server

I have the following code:
Meteor.methods({
saveFile: function(blob, name, path, encoding) {
var path = cleanPath(path), fs = __meteor_bootstrap__.require('fs'),
name = cleanName(name || 'file'), encoding = encoding || 'binary',
chroot = Meteor.chroot || 'public';
// Clean up the path. Remove any initial and final '/' -we prefix them-,
// any sort of attempt to go to the parent directory '..' and any empty directories in
// between '/////' - which may happen after removing '..'
path = chroot + (path ? '/' + path + '/' : '/');
// TODO Add file existance checks, etc...
fs.writeFile(path + name, blob, encoding, function(err) {
if (err) {
throw (new Meteor.Error(500, 'Failed to save file.', err));
} else {
console.log('The file ' + name + ' (' + encoding + ') was saved to ' + path);
}
});
function cleanPath(str) {
if (str) {
return str.replace(/\.\./g,'').replace(/\/+/g,'').
replace(/^\/+/,'').replace(/\/+$/,'');
}
}
function cleanName(str) {
return str.replace(/\.\./g,'').replace(/\//g,'');
}
}
});
Which I took from this project
https://gist.github.com/dariocravero/3922137
The code works fine, and it saves the file, however it repeats the call several time and each time it causes meteor to reset using windows version 0.5.4. The F12 console ends up looking like this: . The meteor console loops over the startup code each time the 503 happens and repeats the console logs in the saveFile function.
Furthermore in the target directory the image thumbnail keeps displaying and then display as broken, then a valid thumbnail again, as if the fs is writing it multiple times.
Here is the code that calls the function:
"click .savePhoto":function(e, template){
e.preventDefault();
var MAX_WIDTH = 400;
var MAX_HEIGHT = 300;
var id = e.srcElement.id;
var item = Session.get("employeeItem");
var file = template.find('input[name='+id+']').files[0];
// $(template).append("Loading...");
var dataURL = '/.bgimages/'+file.name;
Meteor.saveFile(file, file.name, "/.bgimages/", function(){
if(id=="goodPhoto"){
EmployeeCollection.update(item._id, { $set: { good_photo: dataURL }});
}else{
EmployeeCollection.update(item._id, { $set: { bad_photo: dataURL }});
}
// Update an image on the page with the data
$(template.find('img.'+id)).delay(1000).attr('src', dataURL);
});
},
What's causing the server to reset?
My guess would be that since Meteor has a built-in "automatic directories scanning in search for file changes", in order to implement auto relaunching of the application to newest code-base, the file you are creating is actually causing the server reset.
Meteor doesn't scan directories beginning with a dot (so called "hidden" directories) such as .git for example, so you could use this behaviour to your advantage by setting the path of your files to a .directory of your own.
You should also consider using writeFileSync insofar as Meteor methods are intended to run synchronously (inside node fibers) contrary to the usual node way of asynchronous calls, in this code it's no big deal but for example you couldn't use any Meteor mechanics inside the writeFile callback.
asynchronousCall(function(error,result){
if(error){
// handle error
}
else{
// do something with result
Collection.update(id,result);// error ! Meteor code must run inside fiber
}
});
var result=synchronousCall();
Collection.update(id,result);// good to go !
Of course there is a way to turn any asynchronous call inside a synchronous one using fibers/future, but that's beyond the point of this question : I recommend reading this EventedMind episode on node future to understand this specific area.

Resources