I have a MERN stack application which I deployed to Render (server side as web service and front end as static).
In my application, I have a profile where users can upload avatar image. I store an image as a URL string in my MongoDB Atlas_ in User model.
Images are saved on the server in uploads folder. The image upload works fine and image fetches successfully, from both deployment and local environment. But after a while I start to get Internal server error when fetching same image.
What is the reason and how to solve this issue? Or is there a better way of storing images in MongoDB Atlas without using cloud or GridFS?
Here's how I set the user avatar:
const setUserAvatar = async (req, res) => {
try {
if (!req.user || !req.user.id) {
return res.status(401).json({ message: 'Unauthorized' });
}
const user = await User.findById(req.user.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
if (!req.file) {
return res.status(400).json({ message: 'No file uploaded' });
}
const fileName = `avatar_${user.id}_${Date.now()}.png`;
const filePath = path.join(__dirname, '..', 'uploads', fileName);
fs.writeFileSync(filePath, req.file.buffer);
user.avatar = { data: fileName };
await user.save();
res.status(200).json({ message: 'Avatar uploaded successfully' });
} catch (error) {
console.error('Error uploading avatar', error);
res.status(500).json({ message: 'Internal Server Error' });
}
};
And get:
const getUserAvatar = async (req, res) => {
const userId = req.user.id;
try {
const user = await User.findById(userId);
if (user.avatar.data === null) {
return res.status(404).json({ message: 'No avatar found' })
}
if (!user || !user.avatar.data) {
res.send(user.avatar.data)
} else {
const filePath = path.join(__dirname, '..', 'uploads', user.avatar.data);
const avatarData = fs.readFileSync(filePath);
res.setHeader('Content-Type', 'image/*');
res.send(avatarData);
}
} catch (error) {
console.error('Error fetching avatar', error);
res.status(500).json({ message: 'Internal Server Error' });
}
};
How I handle an avatar change on the front end:
const handleAvatarChange = async (e) => {
const token = localStorage.getItem('token');
if (token) {
try {
const formData = new FormData();
formData.append('avatar', e.target.files[0])
const response = await fetch('RENDERLINK/avatar', {
'method': 'POST',
'headers': {
'Authorization': `Bearer ${token}`,
},
'body': formData,
'credentials': 'include'
})
if (response.status === 200) {
getUser()
toast.success('Avatar changed successfully')
} else if (response.status === 400) {
toast.error('No file selected')
} else {
toast.error('Error changing avatar')
}
} catch (error) {
toast.error('Oops! Something went wrong. Please try again later.')
}
} else {
console.log('No token found')
navigate('/login')
}
}
Fetching user data including the avatar:
const getUser = async () => {
const token = localStorage.getItem('token');
if (token) {
try {
const response = await fetch('RENDERLINK/profile', {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
'credentials': 'include'
})
if (response.ok) {
const data = await response.json();
setUser({ username: data.username, email: data.email, avatar: data.avatar.data })
if (data.avatar.data !== null) {
// Fetch and set the avatar image
const avatarResponse = await fetch('RENDERLINK/get_avatar', {
headers: {
'Authorization': `Bearer ${token}`,
},
credentials: 'include',
});
const avatarBlob = await avatarResponse.blob();
const avatarUrl = URL.createObjectURL(avatarBlob);
setUser((prevUser) => ({ ...prevUser, avatar: avatarUrl }));
setLoading(false)
}
} else if (response.status === 500) {
toast.error('Oops! Something went wrong. Please try again later.')
}
} catch (error) {
toast.error('Oops! Something went wrong. Please try again later.')
}
} else {
console.log('No token found')
navigate('/login')
}
}