I’ve built a simple user registration feature in node which stores the user’s name, email, password and an avatar image. I’m using multer to upload the image(in uploads/) and then manually place the file in a subfolder(in uploads/someUsersId/image.jpeg) and added the extension. All this is working fine but i’m storing the avatar image path as:
"C:UsersBlahDesktoptestmulter-testbackenduploads64fb0a40db0abdff97117746avatar-64fb0a40db0abdff97117746.jpeg"
For the frontend, i’m using react with redux-toolkit to store the user in state. I’m trying to read the user data and display it on a page. The text fields (name, email etc.) work fine, however, for the avatar image I get an error:
"Not allowed to load local resource: file:///C:/Users/Blah/Desktop/test/multer-test/backend/uploads/64fb0a40db0abdff97117746/avatar-64fb0a40db0abdff97117746.jpeg"
I’m pretty sure this is not the right way I should be storing the image path to begin with. Can you please recommend the correct way to go about storing image paths so that even after I upload this MERN app to a hosting provider, the image paths work.
Below is the main section of the user registeration controller where I’m handling the filing part:
if (!emailExists) {
let user = await User.create({ name, email, password, userType });
// uploads directory path
const uploadsDirectoryPath = path.resolve(__dirname, '..', 'uploads');
// make user's directory using user id
const userDir = await mkdir(path.join(uploadsDirectoryPath, user.id), { recursive: true });
// copy avatar file into user's directory
await copyFile(path.join(uploadsDirectoryPath, req.file.filename), path.join(userDir, req.file.filename));
// rename avatar file in user's directory
const oldFileExtension = req.file.originalname.split('.')[1];
const newFileName = `avatar-${user.id}.${oldFileExtension}`;
await rename(path.join(userDir, req.file.filename), path.join(userDir, newFileName));
// remove old file
await unlink(path.join(uploadsDirectoryPath, req.file.filename));
// user avatar file path
const avatarPath = path.join(userDir, newFileName);
user.avatar = avatarPath;
await user.save();
if (user) {
return res.status(201).json({
id: user._id,
name: user.name,
email: user.email,
userType: user.userType,
avatar: user.avatar,
token: generateToken(user._id),
});
} else {
res.status(500);
throw new Error('Unable To Register User!');
}
} else {
res.status(400);
throw new Error('Email address is already in use. Please choose another!');
}
2
Answers
I was making a really stupid mistake. To check whether I could directly access the image in the browser I was using
"http://localhost:5000/uploads/64fb0a40db0abdff97117746/avatar-64fb0a40db0abdff97117746.jpeg"
with obviously would not work because in my main.js file, where im creating my express server I used"app.use(express.static('./uploads'))"
. So, in the above image link I had added the extra "uploads" part. Now I can access the image directly in the browser and also in my frontend. However, there seems to be a slight drawback to the approach I am using.To create the avatarPath, I tried using
path.join(serverURL, user.id, newFilename)
but it just returns one slash instead of two, meaning http:localhost. I read online that this is the way path.join() is suppose to work and this is not a bug. Since i'm using Windows then would this not be an issue for a person using MacOS because the path structure is different. One solution which came to mind was to check the user's operating system first and then accordingly form the path. But I feel there is a more elegant method to this problem...Kindly, share if there is a better approach.you just need to save image in folder and in root app/server.js file set path like this
and don’t forget to add your node server domain name with your file location.
Example
Your client is on http://localhost:3000
Your image is on http://localhost:8000
send your image from node server like this :- http://localhost:8000/image/car.png