nodeJS, moving a file to a new folder - node.js

I'm trying to move an existing file into a new folder that doesn't exist. I tried:
var source = fs.createReadStream(file.thumbnail.path);
var dest = fs.createWriteStream('./public/uploads/'+ user._id + '/' + file.myfail.name);
source.pipe(dest);
However, I keep getting this error Error: ENOENT, open './public/uploads/553283d3216c3895055612dd/18f1b232024ac9d7a5d398dc9291e160.jpg'
I also tried using __dirname but it doesn't seem to help.
I'm pretty sure it's a non-existant folder issue, but I'm not sure how to fix it.
PS: after checking if the folder doesn't exist, how do I create it?
thanks

The ENOENT error is because the file or folder does not exist.
The only way to get around this problem is to open a file that does exist. Perhaps the user's folder does not exist before you try this operation? Check to see if the folder exists.
Another thing to note is that Node.js has a path module that provides a lot of useful filepath tools.
var path = require( 'path' )
var destination = path.join( __dirname, 'public/uploads', user._id, file.myfile.name )

You'll want to check if the sub-directory "./public/uploads/" + user._id exists.
If it doesn't exist create the directory before attempting to write out to a directory that doesn't exist.

Related

Understanding how path works in Node

So I have developed a Node Api and on local the path works fine, but I want to move it now to a server online, and don't know how to address the path.
This is the path in local: const path = "/Users/username/Code/projectname/api/invoices"
And what I want to do is make it work also for online, also to make sure that the folder api is what is going to be uploaded, not the one with projectname as it contains the client folder and other folders, so only the api folder.
use __dirname to get the path of the folder.
The __dirname in a node script returns the path of the folder where the current JavaScript file resides.
const path = `${__dirname}/api/invoices`
You can use the __dirname variable.
For this, you first need to import path.
const nodePath = require("path");
Then, to get the path, you need to join the directory name and your custom path together.
const path = nodePath.join(__dirname, "/api/invoices");
This should correctly join the path together.
Note: Naming conventions changed to avoid naming conflicts.
In conclusion, you can use the __dirname variable!

How to share npm modules across projects?

I'm using windows 10 x64.
I'm using dropbox to hold all my projects, and having a 'node_modules' folder inside a dropbox folder is a disaster.
First of all not all npm packages work in a way that would allow this method , because as it says in line one " does not begin with '/', '../', or './' ", so that won't work I think.
So, I tried to make syslink, but as I figured out, windows doesn't allow creating true hard links for folders, only files.
My method to create a folder junction was to have this bat file inside each project folder, and run it as administrator.
SET dest=%~dp0node_modules
SET src=F:\work\node_modules
MKLINK /J %dest% %src%
The bat file works, it creates the syslink, but 3 problems occur.
Dropbox sees it as a real folder and starts syncing it.
That folder doesn't show in the selective syncing to prevent syncing it.
npm can't use it for some reason and when I run 'npm install', It creates a new real 'node_modules' folder to replace the syslink, I get this error output:
I would not try to use junctions, they are pretty hard to work with and can lead to data loss if you don't know what you're doing.
Instead, you could go with the following approach.
Create a json config file containing the path to your modules:
{"modules_root": "F:/work/node_modules/"}
In your code, load this json and require your modules by prepending the path from your config file:
var fs = require("fs");
var modules_root = JSON.parse(fs.readFileSync("modules_root.json")).modules_root;
var express = require(modules_root + "./express")
If you ever decide to use local modules, you could just change the config file to:
{modules_root: "./node_modules/"}

error when moving a file uploaded with multer: resource busy or locked

I have a node+express app that receives files and uploads them to a folder, for which I use multer.
In it's current form, my server.js has:
var multer = require('multer');
app.use(multer({ dest: './uploads/' }).single('files'));
which sends all files to the /uploads directory, with a randomly generated name to avoid conflicts.
That all works fine, however, I need to modify it so that it creates a folder with the randomly generated name and places the file inside (so if a file is randomly named "asdf", then it should end up as uploads/asdf/asdf.
Since the node way of moving files is using fs.rename, I included the following code in my upload route:
fs.rename('uploads/' + newFile.uploadname, 'uploads/' + newFile.uploadname + '/' + newFile.uploadname, function(err) {
if (err) throw err
console.log('Successfully renamed - AKA moved!')
})
(newFile.uploadname should be, and actually is, the name generated by multer).
When that code executes, I get this output:
D:\NodeApp\app\routes\private\upload.js:35
if (err) throw err
^
Error: EBUSY: resource busy or locked, rename 'D:\NodeApp\uploads\b9998bcbb10326c05f305a6a5a0adb9a' -> 'D:\NodeApp\up
loads\b9998bcbb10326c05f305a6a5a0adb9a\b9998bcbb10326c05f305a6a5a0adb9a'
at Error (native)
The file is uploaded properly but not moved, nor the directory created.
What is happening?
The problem was that I was trying to move a file named 'asdf' to a folder called also 'asdf'... Obviously (in restrospect) that folder can't be created because the name is already being used by the file that will be copied. Moving the file to a temporal folder first and then creating the folder solved the issue.

Err. ENOENT when renaming a file in node.js

I'm trying to upload a file in my node/express app, but everytime I've got the ENOENT error when renaming the file. My code is that:
var tmp_path = req.files.file.path;
fs.rename(tmp_path, target_path, function (err) {
if(err) throw err;
...
});
where target_path will be the destination path. If I do:
console.log('exists ' + fs.existsSync(tmp_path));
then my server logs:
exists true
Also, listing the contents of tmp directory shows that the file is there. What's the problem?
FS methods like fs.rename which create, move or rename files expect that any directories in the path already exist. When they do not, you'll get an ENOENT. Since very often what you mean is "make this file -- and any directories in the path I specify for it" you may want to consider using an NPM library that abstracts access to fs with methods that take care of such things.
There are quite a few options. For example fs-extra is one of the better-tested libraries. Using fs-extra you can use ensureDir in that operation to make the directory structure first if it does not yet exist.

Proper way to reference files relative to application root in Node.JS

I have a Node.JS application running on Linux at AWS EC2 that uses the fs module to read in HTML template files. Here is the current structure of the application:
/server.js
/templates/my-template.html
/services/template-reading-service.js
The HTML templates will always be in that location, however, the template-reading-service may move around to different locations (deeper subdirectories, etc.) From within the template-reading-service I use fs.readFileSync() to load the file, like so:
var templateContent = fs.readFileSync('./templates/my-template.html', 'utf8');
This throws the following error:
Error: ENOENT, no such file or directory './templates/my-template.html'
I'm assuming that is because the path './' is resolving to the '/services/' directory and not the application root. I've also tried changing the path to '../templates/my-template.html' and that worked, but it seems brittle because I imagine that is just resolving relative to 'up one directory'. If I move the template-reading-service to a deeper subdirectory, that path will break.
So, what is the proper way to reference files relative to the root of the application?
Try
var templateContent = fs.readFileSync(path.join(__dirname, '../templates') + '/my-template.html', 'utf8');
To get an absolute filesystem path to the directory where the node process is running, you can use process.cwd(). So assuming you are running /server.js as a process which implements /services/template-reading-service.js as a module, then you can do the following from /service/template-reading-service.js:
var appRoot = process.cwd(),
templateContent = fs.readFileSync(appRoot + '/templates/my-template.html', 'utf8');
If that doesn't work then you may be running /service/template-reading-service.js as a separate process, in which case you will need to have whatever launches that process pass it the path you want to treat as the primary application root. For example, if /server.js launches /service/template-reading-service.js as a separate process then /server.js should pass it its own process.cwd().
Accepted answer is wrong. Hardcoding path.join(__dirname, '../templates') will do exactly what is not wanted, making the service-XXX.js file break the main app if it moves to a sub location (as the given example services/template).
Using process.cwd() will return the root path for the file that initiated the running process (so, as example a /Myuser/myproject/server.js returns /Myuser/myproject/).
This is a duplicate of question Determine project root from a running node.js application.
On that question, the __dirname answer got the proper whipping it deserves.
Beware of the green mark, passers-by.
For ES modules, __dirname is not available, so read this answer and use:
import { resolve, dirname, join } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const relativePath = a => join(dirname(fileURLToPath(import.meta.url)), a)
const content1 = fs.readFileSync(relativePath('./file.xyz'), 'utf8') // same dir
const content2 = fs.readFileSync(relativePath('../file.xyz'), 'utf8') // parent dir
We can use path madule to access the current path
const dirname = __dirname;
const path = require('path');
path.resolve(dirname, 'file.txt')
where
dirname - is give us present working directory path name
file.txt - file name required to access

Resources